1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
|
# mcpu-cpp
`mcpu-cpp` — препроцессор языков программирования MCPU. Он является
самостоятельным компонентом экосистемы LibMPU/LibMPUIO/LibMCPU и не привязан
к названию одного конкретного языка: активный язык выбирается директивой
`#lang`.
Этот документ задаёт нормативное поведение `mcpu-cpp`: текстовую модель,
директивы, macro engine, include pipeline, конфигурацию, диагностику и
генерацию зависимостей для инструментов MCPU.
## 1. Текстовая модель
Внешние исходные файлы и конфигурационные файлы имеют кодировку UTF-8.
UTF-8 должен быть корректным. Для исходных программ проверка принадлежности
символов диапазону UCS-2 выполняется после удаления комментариев: поэтому
корректный Unicode scalar value выше `U+FFFF` допустим внутри комментария, но
остаётся ошибкой в программном тексте. После этой стадии исходный текст
обрабатывается как последовательность `__mpu_char16_t`. Входной UTF-8 BOM
допускается и удаляется. Встроенный NUL в исходном файле запрещён.
Переводы строк `CRLF` и `CR` нормализуются в `LF`.
## 2. Действия, выполняемые независимо от директив
`mcpu-cpp` выполняет несколько преобразований до разбора
директив.
### 2.1. Backslash-newline
Последовательность `\\` непосредственно перед переводом строки удаляется до
распознавания комментариев, директив и макросов. Поэтому, например,
```text
#defi\
ne FOO 10\
20
```
эквивалентно логической строке
```text
#define FOO 1020
```
При этом физические номера строк продолжают учитываться при формировании
текущей позиции; если пользователь не менял её директивой `#line`, они и будут
видны в генерируемых line marker-ах.
### 2.2. Комментарии
Комментарии `/* ... */` и `// ...` удаляются до последующей обработки. Там,
где это необходимо для разделения соседних токенов, сохраняется пробельный
разделитель. Если комментарий завершает непустую строку, после его удаления не
сохраняются ни синтетический разделитель, ни пробелы, предшествовавшие
комментарию: строка заканчивается последним значащим символом. Это относится и
к многострочному комментарию, начавшемуся после программного текста. Если после
удаления комментария строка вообще не содержит ничего кроме пробелов, она
становится действительно пустой строкой. При этом комментарий между двумя
токенами по-прежнему оставляет необходимый разделитель и не склеивает их.
Переводы строк сохраняются, чтобы не разрушать координаты исходного текста.
Комментарий не распознаётся внутри строковой или символьной константы. Для
языка `diff` апостроф не считается началом символьной константы, поскольку
используется в обозначениях производных.
В буквальном аргументе `#include <...>` последовательности `/*` и `//`
рассматриваются как часть имени файла.
## 3. Директивы и выходной поток
Директива начинается символом `#`, если до него в логической строке находятся
только пробельные символы или комментарии. Между `#` и именем директивы
допускаются пробелы.
Служебная информация о позиции в выходном потоке представлена в форме GNU
**line marker**:
```text
# номер "имя-файла" [флаги]
```
Это не входная директива `#line`. При входе во включаемый файл к line marker-у
добавляется флаг `1`, а при возврате в файл, содержащий `#include`, — флаг `2`.
Эти значения имеют тот же смысл, что и в GNU CPP: `1` означает вход в новый
файл, `2` — возврат в предыдущий файл. Флаг `2` не является числом или уровнем
вложенности.
Например:
```text
# 1 "main.c"
# 1 "defs.h" 1
...
# 2 "main.c" 2
```
Входная директива
```text
#line 62 "main.y"
```
сама в выходной поток не копируется. Она изменяет логические значения
`__LINE__` и `__FILE__` для последующего текста, а в выходе представляется
line marker-ом:
```text
# 62 "main.y"
```
Аргументы `#line` предварительно подвергаются macro expansion, как в
принятой модели line control. Если после такого `#line` происходит `#include`, то после
возврата marker получает флаг `2`, например `# 65 "main.y" 2`. Имя, заданное
через `#line`, становится логическим именем для `__FILE__` и line marker-ов; оно
не меняет каталог, относительно которого ищется quoted `#include`.
Директивы препроцессора имеют только канонические английские имена. Unicode остаётся полностью допустимым в идентификаторах, строках, комментариях и другом пользовательском тексте.
## 4. Заголовочные файлы
Поддерживаются:
```text
#include "file"
#include <file>
#include_next "file"
#include_next <file>
#pragma once
```
Для обычного `#include "file"` первым всегда проверяется каталог **физического**
текущего исходного файла. Логическое имя, установленное через `#line`, на этот
шаг не влияет. Для `#include <file>` каталог текущего файла не проверяется.
### 4.1. Перемещаемый корень MCPU как общий принцип экосистемы
Начиная с выпуска 0.0.37 каталог установки MCPU **не содержит версию
конкретного инструмента** и не является абсолютной runtime-константой,
зашитой в бинарный файл. Версия относится к самому `mcpu-cpp`, `mcpu-as`,
`mcpu-ld`, `mcpu-run` или библиотеке, но не определяет корень единой среды
MCPU.
При типичной конфигурации:
```text
./configure --prefix=/usr --libdir=/usr/lib64
```
`make install` создаёт дерево:
```text
/usr/lib64/mcpu/
├── bin/
│ └── mcpu-cpp
├── etc/
│ └── mcpu-cpp.conf
├── include/
│ ├── diff/
│ ├── dift/
│ ├── alg/
│ ├── as/
│ ├── avm/
│ └── acs/
└── lib/ # общий каталог будущих библиотек MCPU
```
Публичное имя программы находится в `$bindir`:
```text
/usr/bin/mcpu-cpp -> ../lib64/mcpu/bin/mcpu-cpp
```
Абсолютный `/usr/lib64/mcpu` при этом **не является частью runtime ABI
MCPU-CPP**. Он используется только `make install` как выбранное configure-time
место размещения файлов.
При каждом обычном запуске MCPU-CPP определяет фактический путь собственного
исполняемого файла через Linux `/proc/self/exe`. Символическая ссылка публичной
команды не мешает этому: `/proc/self/exe` указывает на реально выполняемый
бинарный файл. Если `/proc/self/exe` недоступен, используется резервное
разрешение `argv[0]` через `PATH` и `realpath(3)`; возврата к зашитому
configure-time installation root нет.
Для бинарного файла:
```text
<root>/bin/mcpu-cpp
```
runtime-корень определяется как:
```text
executable = <root>/bin/mcpu-cpp
executable dir = <root>/bin
MCPU runtime root = <root>
```
Из него автоматически выводятся:
```text
<root>/etc/mcpu-cpp.conf
<root>/include
```
Следовательно всё дерево можно физически перенести, например из:
```text
/usr/lib64/mcpu/
```
в:
```text
/opt/mcpu-test/
```
или:
```text
$HOME/devel/mcpu-next/
```
и `<new-root>/bin/mcpu-cpp` без переконфигурирования начнёт использовать
`<new-root>/etc/mcpu-cpp.conf` и `<new-root>/include`. Старый абсолютный путь
не сохраняется ни в runtime default, ни в штатном `mcpu-cpp.conf`.
Это не частная особенность препроцессора, а **общий принцип экосистемы MCPU**.
Будущие `mcpu-as`, `mcpu-ld`, `mcpu-run`, библиотеки, CRT и другие компоненты
должны разделять один перемещаемый корень:
```text
<root>/bin
<root>/etc
<root>/include
<root>/lib
```
Их собственные версии могут отличаться, но согласованность конкретной среды
MCPU определяется тем, что все компоненты находятся в одном runtime tree, а
не совпадением version suffix в именах каталогов.
### 4.2. Runtime defaults, уровни конфигурации и системный include root
До чтения любого конфигурационного файла MCPU-CPP создаёт runtime-derived
значение:
```text
MCPU_CPP_SYSTEM_INCLUDE_PATH = <runtime-root>/include
```
После этого конфигурационные слои применяются в порядке возрастающего
приоритета:
```text
runtime-derived defaults
↓
<runtime-root>/etc/mcpu-cpp.conf
↓
/etc/mcpu/mcpu-cpp.conf
↓
$HOME/.mcpu/etc/mcpu-cpp.conf
```
`<runtime-root>/etc/mcpu-cpp.conf` устанавливается вместе с MCPU-CPP, но сам
файл намеренно не содержит абсолютного штатного `MCPU_CPP_SYSTEM_INCLUDE_PATH`:
иначе перенос всего дерева восстановил бы старый путь. `/etc/mcpu/mcpu-cpp.conf`
является необязательным machine-wide override: `make install` каталог
`/etc/mcpu` не создаёт. Домашний `$HOME/.mcpu/etc/mcpu-cpp.conf` также необязателен,
не версионируется и имеет максимальный config-приоритет.
Если одна переменная определена несколько раз, побеждает последнее
определение, включая пустое. Поэтому `MCPU_CPP_SYSTEM_INCLUDE_PATH` остаётся
полностью заменяемым system root. Например:
```text
MCPU_CPP_SYSTEM_INCLUDE_PATH = $HOME/mcpu-next/include;
```
полностью заменяет runtime-derived `<runtime-root>/include`. Для активного
`#lang "as"` тогда проверяются:
```text
$HOME/mcpu-next/include/as
$HOME/mcpu-next/include
```
Штатные language-подкаталоги всегда выводятся самим препроцессором из одного
root; переменных вида `MCPU_CPP_SYSTEM_<LANG>_INCLUDE_PATH` нет.
Пустое effective значение:
```text
MCPU_CPP_SYSTEM_INCLUDE_PATH = ;
```
удаляет configured system stage полностью. Более приоритетный config может
после этого снова включить его непустым значением.
`--config-file FILE` применяет явно выбранный файл поверх runtime-derived
default. `--no-config` отключает **только чтение файлов конфигурации**:
`<runtime-root>/etc/mcpu-cpp.conf`, `/etc/mcpu/mcpu-cpp.conf` и
`$HOME/.mcpu/etc/mcpu-cpp.conf` не читаются, но `<runtime-root>/include` остаётся
штатным system root.
`--sys-root=PATH` является более сильным command-line override. Он считает
`PATH` системным корнем MCPU, формирует runtime-derived
`MCPU_CPP_SYSTEM_INCLUDE_PATH` как `PATH/include` и неявно отключает чтение всех
configuration files, включая явно заданный `--config-file`. `PATH` может быть
абсолютным или относительным. Относительное значение разрешается относительно
рабочего каталога, из которого была запущена команда, до формирования
effective include root. Это позволяет для одного запуска выбрать другое полное
дерево MCPU без изменения установленного дерева и persistent configuration.
Только `-nostdinc` удаляет effective standard-system tree из include search для
конкретного запуска; явно переданный `-isystem` при этом остаётся command-line
каталогом.
### 4.3. Нормативный порядок поиска include-файлов
Порядок поиска является частью контракта MCPU-CPP. Явно заданные параметры
командной строки имеют приоритет над persistent configuration. После
необязательного каталога текущего физического файла эффективная цепочка имеет
строго следующий вид:
```text
explicit -I
↓
explicit -isystem
↓
MCPU_CPP_<LANG>_INCLUDE_PATH
↓
MCPU_CPP_INCLUDE_PATH
↓
MCPU_CPP_SYSTEM_INCLUDE_PATH/<lang>
↓
MCPU_CPP_SYSTEM_INCLUDE_PATH
↓
explicit -idirafter
↓
MCPU_CPP_AFTER_INCLUDE_PATH
```
Элементы, которых нет или которые не содержат требуемого файла, пропускаются.
`MCPU_CPP_<LANG>_INCLUDE_PATH` — свободно настраиваемые пользователем
language-specific path-list'ы:
```text
MCPU_CPP_DIFF_INCLUDE_PATH
MCPU_CPP_DIFT_INCLUDE_PATH
MCPU_CPP_ALG_INCLUDE_PATH
MCPU_CPP_AS_INCLUDE_PATH
MCPU_CPP_AVM_INCLUDE_PATH
MCPU_CPP_ACS_INCLUDE_PATH
```
Пользователь полностью распоряжается именами и расположением этих каталогов.
`MCPU_CPP_INCLUDE_PATH` — общий пользовательский path-list, видимый во всех
языковых состояниях.
`-idirafter` и `MCPU_CPP_AFTER_INCLUDE_PATH` являются общим fallback-карманом.
MCPU-CPP не строит для них автоматических `<lang>`-подкаталогов. Пользователь
сам организует их внутреннюю структуру и при необходимости пишет, например:
```text
#include <vendor/device.h>
```
Именно semantic class, а не порядок появления разных классов в argv/config,
определяет приоритет. Внутри одного класса сохраняется порядок добавления.
### 4.4. `#include_next` и wrapper headers
`#include_next` предназначен прежде всего для заголовков-обёрток (wrapper
headers). Он позволяет поставить локальный header раньше системного, изменить
локальную политику и затем продолжить поиск одноимённого header по нормативной
цепочке без копирования системного файла и без абсолютного имени.
Например:
```text
mcpu-cpp -isystem $HOME/mcpu-wrapper ...
```
и `$HOME/mcpu-wrapper/math.h`:
```text
#ifndef SOME_SYSTEM_MACRO
#define SOME_SYSTEM_MACRO temporary_value
#define REMOVE_SOME_SYSTEM_MACRO 1
#endif
#include_next <math.h>
#ifdef REMOVE_SOME_SYSTEM_MACRO
#undef SOME_SYSTEM_MACRO
#undef REMOVE_SOME_SYSTEM_MACRO
#endif
```
Если домашний config одновременно задаёт:
```text
MCPU_CPP_SYSTEM_INCLUDE_PATH = $HOME/mcpu-next/include;
```
wrapper найденный через `-isystem` продолжит `#include_next` уже через
configured user paths, затем через
`$HOME/mcpu-next/include/<lang>` и `$HOME/mcpu-next/include`; старое system tree исходного места установки при этом не участвует. Именно такой сценарий позволяет
системному разработчику или тестеру жить в собственной sandbox.
MCPU-CPP хранит конкретный **физический элемент effective search chain**, из
которого найден текущий header. `#include_next` начинает со следующего элемента.
Формы `"file"` и `<file>` для `#include_next` эквивалентны; каталог текущего
файла повторно не проверяется. Если текущий файл найден обычным quoted-поиском
относительно содержащего файла и не имеет search-chain provenance,
`#include_next` начинает с первого элемента configured chain.
Операнд может быть получен macro expansion. Логическое имя после `#line` не
влияет на физический provenance. Если после текущего entry подходящего файла
нет, preprocessing завершается ошибкой.
### 4.5. `#pragma once`
Активная директива
```text
#pragma once
```
помечает **физический файл** как уже обработанный в текущем запуске
MCPU-CPP. При последующей попытке включить тот же физический файл его
содержимое повторно не обрабатывается. Сама директива потребляется
препроцессором и в выходной поток не копируется, в том числе при `-dD`.
Идентичность определяется по паре `st_dev`/`st_ino`, полученной файловой
системой, а не по строковому имени пути. Поэтому один и тот же файл не может
обойти `#pragma once`, если он достигнут как `./file.h`, через символическую
ссылку или через другое жёсткое имя (hard link). Логическое имя после `#line`
также не влияет на эту физическую идентичность.
Пометка действует сразу в момент обработки активной директивы. Поэтому
заголовок может после `#pragma once` включить самого себя: повторное включение
будет пропущено и рекурсия не возникнет. Директива внутри неактивной ветви
условной компиляции никакого действия не имеет.
MCPU-CPP распознаёт только точную форму `#pragma once` с необязательными
пробелами. Остальные `#pragma` не интерпретируются препроцессором и сохраняются
для последующих стадий компиляции; например, `#pragma pack(...)` продолжает
передаваться в выходной поток.
`#pragma once` дополняет, но не изменяет нормативную search-chain
`#include`/`#include_next`: сначала обычный механизм поиска находит физический
файл, затем registry `once` решает, надо ли обрабатывать его содержимое.
### 4.6. Принудительные файлы: `-imacros FILE` и `-include FILE`
Опции командной строки
```text
-imacros FILE
-include FILE
```
обрабатывают файл до главного input. Они используют обычный preprocessing
engine, а не отдельный облегчённый parser.
Нормативный порядок начала translation unit:
```text
predefined macros
-> -D/-U в порядке командной строки
-> все -imacros в порядке командной строки
-> все -include в порядке командной строки
-> главный input
```
Таким образом, взаимное расположение `-imacros` и `-include` в `argv` не
перемешивает эти две группы: **все** `-imacros` всегда выполняются раньше
**всех** `-include`.
`-imacros FILE` полностью обрабатывает файл: его `#define`/`#undef`,
условные директивы, `#lang`/`#endlang`, `#include`, `#include_next`,
`#pragma once` и диагностика имеют обычную семантику. Однако весь normal
preprocessing output этого forced-файла, включая line markers и текст
вложенных headers, отбрасывается. Полученное состояние macro table и других
preprocessing-механизмов сохраняется для последующих forced-файлов и главного
input.
`-include FILE` использует тот же механизм, но normal output сохраняется, как
если бы найденный header был включён непосредственно перед главным source.
Forced include является настоящей include-границей: внутри него
`__INCLUDE_LEVEL__ == 1`, внутри включённого им header уровень равен `2`, а
главный input остаётся на уровне `0`. `__BASE_FILE__` внутри forced-файлов
остаётся именем главного input.
Абсолютный operand forced-файла используется непосредственно. Относительный
operand ищется сначала в **current working directory**, затем по обычной
include-chain:
```text
explicit -I
explicit -isystem
MCPU_CPP_<LANG>_INCLUDE_PATH
MCPU_CPP_INCLUDE_PATH
MCPU_CPP_SYSTEM_INCLUDE_PATH/<lang>
MCPU_CPP_SYSTEM_INCLUDE_PATH
explicit -idirafter
MCPU_CPP_AFTER_INCLUDE_PATH
```
Каталог главного input не получает специального приоритета при поиске operand
`-imacros`/`-include`. После нахождения forced-файла обычный quoted
`#include "file"` внутри него снова разрешается относительно физического
каталога этого файла. Если forced-файл найден через элемент include-chain,
его provenance сохраняется и `#include_next` продолжает поиск со следующего
элемента цепочки.
Forced-файлы и реально достигнутые из них headers входят в обычный physical
dependency registry. Их user/system classification определяется тем же
search provenance, поэтому `-MM`/`-MMD` фильтруют system forced headers так же,
как обычные system headers. Отсутствующий forced-файл является ошибкой.
### 4.7. Генерация зависимостей: `-M`, `-MM`, `-MG`, `-MD`, `-MMD`, `-MF`, `-MT`, `-MQ`
Опции `-M` и `-MM` используют **тот же самый проход include pipeline**, что и
обычная preprocessing. Отдельного повторного поиска заголовков не выполняется.
Поэтому dependency graph автоматически наследует нормативный порядок путей,
`#include_next`, macro-expanded include operands, conditional compilation и
`#pragma once`.
`-M` подавляет обычный preprocessing output и выводит одно правило Make:
```make
file.o: file.c header1.h header2.h
```
В список входят главный source-файл и все реально достигнутые физические
headers, включая system headers. Один физический файл записывается один раз;
идентичность определяется как `st_dev + st_ino`, поэтому другое относительное
имя, symbolic link или hard link не создают дополнительную dependency. Имя,
назначенное директивой `#line`, является только logical source name и в
dependency list не попадает.
`-MM` строит тот же граф, но исключает system dependencies. System-контекстом
считаются headers, найденные через explicit `-isystem`, configured system tree
`MCPU_CPP_SYSTEM_INCLUDE_PATH/<lang>` / `MCPU_CPP_SYSTEM_INCLUDE_PATH`, explicit
`-idirafter` и `MCPU_CPP_AFTER_INCLUDE_PATH`, а также вся ветвь headers,
включённая непосредственно или косвенно из такого system header. Форма
`#include "file"` или `#include <file>` сама по себе не определяет system-ness.
Если один и тот же физический файл был достигнут из system-ветви, но затем
также включён непосредственно из user-контекста, он остаётся пользовательской
dependency и присутствует в `-MM`.
Default target образуется из basename главного source-файла: его suffix
заменяется object suffix (`.o` по умолчанию). Пути и target экранируются для
Make. Для stdin используется GNU-подобная форма `-: -`.
`-MD` и `-MMD` используют тот же dependency graph, но, в отличие от `-M` и
`-MM`, **не подавляют обычный preprocessing output**. `-MD` включает system
headers, как `-M`; `-MMD` применяет user-only фильтр `-MM`. Это позволяет одним
проходом получить и препроцессированный текст, и side-effect dependency file.
Если `-MF` не задан, side-effect режим выбирает имя `.d` автоматически:
* без `-o` из basename входного файла удаляется suffix и добавляется `.d`;
каталоги входного pathname в имя dependency-файла не переносятся;
* при обычном `-o FILE` suffix output-файла заменяется на `.d`;
* для stdin используется имя `-.d`.
`-MF FILE` переопределяет автоматическое имя dependency-файла. Значение
`-MF -` означает stdout. `-MF` работает также с dependency-only `-M`/`-MM`;
в этом случае оно имеет приоритет над обычным destination для make-rule. Само
по себе `-MF` без одного из `-M`, `-MM`, `-MD`, `-MMD` является ошибкой.
Семантика намеренно следует GNU CPP: `-MD`/`-MMD` не принимают собственный
аргумент, а `-MF` является отдельной опцией назначения dependency output.
`-MT TARGET` заменяет автоматический target правила строкой `TARGET` **точно
как она передана**. Make quoting при этом не выполняется. Поэтому один argument
`-MT` может сам содержать несколько targets, разделённых пробелами:
```text
-MT 'obj/a.o obj/a.pic.o'
```
и повторные `-MT` также добавляют targets одного и того же правила:
```text
-MT obj/a.o -MT obj/a.pic.o
```
`-MQ TARGET` имеет ту же семантику выбора target, но экранирует специальные
для Make символы. Например,
```text
-MQ '$(OBJDIR)/foo.o'
```
даёт левую часть правила:
```make
$$(OBJDIR)/foo.o:
```
Поддерживаются как отдельные arguments (`-MT TARGET`, `-MQ TARGET`), так и
attached forms (`-MTTARGET`, `-MQTARGET`).
Если задан хотя бы один `-MT` или `-MQ`, автоматический default target не
выводится. В частности, `--object-suffix` влияет только на автоматический
target и не переписывает явно заданные targets. Если явных targets нет,
default target экранируется для Make так же, как при `-MQ`.
Разрешены повторные и смешанные `-MT`/`-MQ`. В соответствии с GNU CPP сначала
выводятся все `-MT` targets в их command-line order, затем все `-MQ` targets в
их command-line order. Все они образуют левую часть **одного** dependency
rule.
`-MT` и `-MQ` имеют смысл только вместе с одним из dependency-generation
режимов `-M`, `-MM`, `-MD` или `-MMD`. Без такого режима это ошибка командной
строки.
`-MG` изменяет только обработку **отсутствующих** include-файлов при
dependency-only режимах `-M` и `-MM`. Без `-MG` неразрешённый `#include` остаётся
ошибкой. С `-M -MG` или `-MM -MG` отсутствующий header считается будущим
generated file: preprocessing не завершается ошибкой, а operand директивы
добавляется в dependency rule **ровно в том виде, который получен после macro
expansion**, без приписывания предполагаемого include-directory. Например:
```text
#include "generated.h"
```
при `-M -MG` добавляет dependency `generated.h`, даже если такого файла ещё нет.
Macro-expanded include ведёт себя аналогично: dependency получает уже
развёрнутое имя. `-MG` разрешён только вместе с `-M` или `-MM`; комбинации с
`-MD`/`-MMD` и использование без dependency-only режима являются ошибкой
командной строки.
Unresolved dependencies интегрированы в **тот же упорядоченный dependency
registry**, что и физически найденные файлы, но образуют отдельный identity
domain. Для найденного файла registry по-прежнему использует `st_dev/st_ino` и
physical provenance. Для отсутствующего файла этих данных нет, поэтому `-MG`
entry не выполняет `stat()` и дедуплицируется по точному тексту include operand.
Это принципиально: наличие одноимённого файла в CWD не должно превращать
неразрешённый `<name>` в ложное physical совпадение, если angle-search этот файл
не находил. Разные unresolved spellings (`generated.h` и `./generated.h`)
считаются разными dependencies.
Для `-MM` unresolved dependency получает user/system class из контекста поиска:
отсутствующий `<file>` является system-class, отсутствующий `"file"` — user-class,
если сама включающая единица не является system header; любой missing include,
достигнутый из system header, остаётся system-class. При повторении одного и того
же unresolved operand сохраняется классификация его первого появления, что
соответствует GNU CPP. Физически найденные зависимости сохраняют прежнее правило:
если один и тот же inode позднее достигается из user-контекста, он перестаёт быть
system-only.
`-MG` распространяется также на отсутствующие command-line forced files
`-include FILE` и `-imacros FILE`: их operand заносится в unresolved registry как
user dependency без синтетического search prefix. При наличии реального файла
`-include`/`-imacros` продолжают использовать обычный physical dependency
registry и search provenance.
## 5. Переключение языков
Препроцессор запускается в состоянии `0`. Это безымянный основной C-подобный
язык и он не является допустимым аргументом `#lang`.
Допустимые языки:
| Имя | Назначение |
|---|---|
| `diff` | дифференциальные уравнения |
| `dift` | разностные уравнения |
| `alg` | алгебраические уравнения |
| `as` | MCPU assembler (`mcpu-as`) |
| `avm` | схемы аналоговых вычислительных машин |
| `ACS` | структурные схемы систем автоматического управления |
После `#lang` обязательна строковая константа с одним непустым словом:
```text
#lang "diff"
```
Имя проверяется только по внутреннему списку языков выше и сравнивается без
учёта ASCII-регистра. Поэтому `"diff"`, `"Diff"`, `"DIFF"` и `"dIfF"`
эквивалентны при выборе языка. Исходное написание внутри кавычек при этом
сохраняется в выходном потоке.
Пробелы внутри строковой константы запрещены: `" diff"`, `"diff "` и
`"di ff"` являются ошибками. Escape-последовательности внутри неё не
разбираются. Закрывающая кавычка обязана находиться на той же физической строке
исходного файла. После неё до конца строки допустимы только пробельные символы.
Внешние пробелы директивы нормализуются. Например:
```text
# lang "DiFf"
```
превращается в:
```text
#lang "DiFf"
```
`#lang` помещает новый язык в стек, `#endlang` восстанавливает предыдущий.
Стек не сбрасывается при `#include`, поэтому начало и конец языкового блока
могут находиться в разных файлах. Директивы `#lang` и `#endlang` сохраняются в
выходном потоке для последующего frontend dispatcher; `#lang` сохраняется в
нормализованной форме.
## 6. Простые макроопределения
Начиная с 0.0.4 поддерживаются object-like macros:
```text
#define BUFFER_SIZE 1024
#define NAME value
#define EMPTY
```
Директива `#define` сама в выходной поток не попадает. В обычном тексте
идентификатор-макро заменяется его replacement list. Replacement затем снова
просматривается на макроимена, поэтому допускается каскадное раскрытие:
```text
#define A B
#define B 10
A
```
даёт `10`.
Во время раскрытия конкретное макро временно блокируется. Поэтому
самоссылочные и взаимно-рекурсивные определения не вызывают бесконечной
рекурсии.
Макроимена не раскрываются внутри строковых и символьных констант. Для `diff`
апостроф сохраняет специальную языковую семантику и не защищает последующий
текст как C character constant.
Многострочное определение через backslash-newline поддерживается, поскольку
splice выполняется раньше `#define`.
### 6.1. `#undef`
```text
#undef NAME
```
удаляет object-like macro. Отмена несуществующего определения не является
ошибкой.
### 6.2. Вычисляемый `#include`
Аргумент `#include`, который не начинается непосредственно с `"` или `<`,
сначала проходит macro expansion. Поэтому допустимо:
```text
#define HEADER <diff/model.h>
#include HEADER
```
или
```text
#define HEADER "local.h"
#include HEADER
```
Результат раскрытия обязан иметь форму `"file"` или `<file>`.
## 7. Макро с аргументами
Macro engine поддерживает
function-like macros:
```text
#define идентификатор( список аргументов ) текст
```
Открывающая скобка в определении должна идти **непосредственно**
после имени макро. Поэтому
```text
#define F(X) X
```
задаёт макро с аргументом, а
```text
#define F (X)
```
задаёт простое object-like macro со строкой замены `(X)`.
В месте использования между именем function-like macro и открывающей скобкой
пробельные символы допустимы. Если `(` не следует, идентификатор не считается
вызовом данного макро и остаётся в выходном тексте.
Для обычного function-like macro число фактических аргументов должно совпадать
с числом формальных. Для variadic macro должны присутствовать все фиксированные
аргументы, а variadic tail может содержать произвольное число аргументов, включая
пустой tail. При разборе списка фактических аргументов вложенные круглые скобки
учитываются; запятая внутри них не разделяет аргументы. Квадратные скобки такого
свойства не имеют — это является частью принятой семантики macro expansion.
Например,
```text
#define min(X, Y) ((X) < (Y) ? (X) : (Y))
min(1, 2)
```
даёт
```text
((1) < (2) ? (1) : (2))
```
Перед подстановкой обычный фактический аргумент сам проходит macro expansion.
Поэтому каскадные и вложенные вызовы работают естественно:
```text
#define A 7
#define min(X, Y) ((X) < (Y) ? (X) : (Y))
min(min(A, 3), 10)
```
Формальный параметр может встречаться в replacement list произвольное число
раз. Это означает, что выражение с побочным эффектом в
фактическом аргументе также может быть вычислено несколько раз уже последующим
компилятором; препроцессор не пытается исправлять такую программу.
Поддерживаются макро без формальных параметров:
```text
#define READY() 1
```
Они раскрываются только как вызов `READY()` (пробел между именем и `(` при
использовании допустим), но самостоятельный идентификатор `READY` не
раскрывается.
Имена формальных параметров должны быть различны. Незавершённый список,
неверная пунктуация, недостаточное или избыточное число фактических аргументов
диагностируются как ошибки.
### 7.1. Stringification `#`
Поддерживается оператор
stringification (`#`) для параметров function-like macro:
```text
#define STR(X) #X
STR(alpha + beta)
```
даёт
```text
"alpha + beta"
```
Stringification использует **сырой фактический аргумент до macro expansion**.
Поэтому:
```text
#define A 7
#define STR(X) #X
#define XSTR(X) STR(X)
STR(A) -> "A"
XSTR(A) -> "7"
```
Ведущие и завершающие пробелы аргумента удаляются. Последовательности
пробельных символов внутри аргумента сворачиваются в один пробел, кроме
пробелов внутри строковых/символьных токенов соответствующего активного
языка. Двойные кавычки и обратные косые черты внутри quoted tokens экранируются
так, чтобы результат оставался одной корректной строковой константой.
Оператор `#` в replacement list function-like macro обязан непосредственно или
через пробельные символы ссылаться на имя формального параметра. Внутри quoted
token символ `#` оператором не является. Пустой фактический аргумент допустим и
stringify-ится как `""`.
### 7.2. Token concatenation `##`
Начиная с 0.0.21 поддерживается оператор token concatenation (`##`) в модели,
согласованной с GNU CPP и механизмом `collect_expansion()` / `macroexpand()`
macro engine. Оператор объединяет два соседних preprocessing token в
один token, после чего получившийся replacement list снова проходит macro
expansion.
Например:
```text
#define CAT(A, B) A ## B
CAT(foo, bar)
```
даёт `foobar`. Склеивание может образовывать identifier, preprocessing number
или многосимвольный punctuator. Поэтому, например, допустимы:
```text
CAT(1.5, e3) -> 1.5e3
CAT(+, =) -> +=
```
Если формальный параметр непосредственно примыкает к `##`, его фактический
аргумент подставляется **без предварительного macro expansion**. Это тот же
raw-argument принцип, который используется для stringification. Для получения
сначала expansion, а затем concatenation применяется обычный двухуровневый
приём GNU CPP:
```text
#define AFTERX(X) X_ ## X
#define XAFTERX(X) AFTERX(X)
#define TABLESIZE 1024
#define BUFSIZE TABLESIZE
AFTERX(BUFSIZE) -> X_BUFSIZE
XAFTERX(BUFSIZE) -> X_1024
```
Пустой фактический аргумент рядом с `##` ведёт себя как placemarker: сам по
себе он не добавляет token, а `##` с такой стороны не изменяет оставшийся
операнд. Если фактический аргумент содержит несколько preprocessing tokens,
склеивается только крайний token, непосредственно соседний с `##`; остальные
tokens сохраняются и затем участвуют в общем rescan.
`#` и `##` могут использоваться в одном function-like macro, например:
```text
#define COMMAND(NAME) #NAME | NAME ## _command
```
При этом `#NAME` использует raw spelling аргумента для stringification, а
`NAME ## _command` — тот же raw argument для concatenation.
`##` внутри quoted token оператором не является. Комментарии к моменту macro
expansion уже заменены whitespace, поэтому они не могут быть созданы
склеиванием `/` и `*`. Между `##` и его операндами исходно может находиться
whitespace; при склеивании он не участвует.
Если два операнда не образуют один допустимый preprocessing token, выдаётся
диагностика, а сами исходные tokens сохраняются; наличие whitespace между ними
после такой диагностики не является частью контракта. `##` в начале или в
конце replacement list является ошибкой определения macro.
### 7.3. Variadic macros: `...` и `__VA_ARGS__`
Начиная с 0.0.46 поддерживаются variadic function-like macros в современной
C99-совместимой форме:
```text
#define LOG(...) output(__VA_ARGS__)
#define LOGF(format, ...) output(format, __VA_ARGS__)
```
Маркер `...` может быть единственным параметром либо последним элементом после
одного или нескольких фиксированных параметров. Старое GNU-расширение с
именованным variadic parameter
```text
#define LOG(args...) ...
```
в 0.0.46 намеренно не поддерживается. `__VA_OPT__` также не является частью
этого релиза.
При вызове все tokens после последнего фиксированного параметра, включая
разделяющие их запятые, образуют один logical variable argument и подставляются
вместо `__VA_ARGS__`. В обычной позиции этот variable argument предварительно
проходит macro expansion так же, как обычный фактический аргумент:
```text
#define A 7
#define V(...) <__VA_ARGS__>
#define F(first, ...) first | __VA_ARGS__
V(A, 2, 3) -> <7, 2, 3>
F(1, A, 3) -> 1 | 7, 3
```
Variadic tail может быть пустым. Поэтому оба вызова
```text
F(1)
F(1,)
```
допустимы и подставляют пустой `__VA_ARGS__`. Это **не** означает автоматическое
удаление запятой, явно записанной в replacement list. Например для
```text
#define E(format, ...) output(format, __VA_ARGS__)
```
вызов `E("ok")` оставляет запятую перед пустым tail. Специальная историческая
GNU-семантика `, ## __VA_ARGS__`, удаляющая такую запятую, в контракт 0.0.46 не
входит; если она понадобится, её следует вводить отдельным явно документированным
расширением.
`__VA_ARGS__` участвует в уже существующей семантике `#` и `##` как настоящий
macro parameter. Stringification использует raw spelling всего variadic tail:
```text
#define STRV(...) #__VA_ARGS__
STRV(A, b + c) -> "A, b + c"
```
При соседстве с `##` variadic argument также подставляется без prescan; затем
работают обычные правила placemarker, token concatenation и общего rescan.
Например:
```text
#define L(...) pre ## __VA_ARGS__
#define R(...) __VA_ARGS__ ## post
L(fix) -> prefix
R(fix) -> fixpost
```
Если variadic argument содержит несколько preprocessing tokens, склеивается
только крайний token, непосредственно соседний с `##`, а остальные tokens
сохраняются, как и для обычного параметра. Пустой tail рядом с `##` ведёт себя
как placemarker.
Имя `__VA_ARGS__` зарезервировано для variable argument и не принимается как
обычное имя формального параметра. Оператор `#__VA_ARGS__` допустим только в
variadic macro. Dump-режимы сохраняют variadic форму определения, например:
```text
#define F(first,...) first | __VA_ARGS__
```
### 7.4. `__VA_OPT__`
Начиная с 0.0.47 variadic macros поддерживают стандартный условный fragment
`__VA_OPT__(pp-tokens)`. Если variable argument после обычной macro substitution
не содержит preprocessing tokens, весь `__VA_OPT__(...)` раскрывается в пустую
последовательность. Если variable argument непуст, содержимое круглых скобок
участвует в replacement list:
```text
#define DEBUG(format, ...) \
fprintf(stderr, format __VA_OPT__(,) __VA_ARGS__)
DEBUG("ready") -> fprintf(stderr, "ready")
DEBUG("x=%d", x) -> fprintf(stderr, "x=%d", x)
```
Решение о непустоте принимается **после expansion variable argument**, а не по
его исходному spelling. Поэтому macro, который сам раскрывается в пустую
последовательность, не активирует `__VA_OPT__`:
```text
#define EMPTY
#define HAS(...) [__VA_OPT__(yes)]
HAS() -> []
HAS(EMPTY) -> []
HAS(token) -> [yes]
```
Содержимое `__VA_OPT__` может включать сбалансированные вложенные круглые скобки.
Закрывающая `)` самого `__VA_OPT__` определяется с учётом их вложенности.
Вложенный `__VA_OPT__` внутри другого `__VA_OPT__` намеренно запрещён.
`__VA_OPT__` интегрирован с существующими правилами parameter substitution,
stringification, token concatenation, placemarker и rescan. Например:
```text
#define X 123
#define S(...) #__VA_OPT__(__VA_ARGS__)
#define L(...) pre ## __VA_OPT__(__VA_ARGS__)
S() -> ""
S(X) -> "123"
L() -> pre
L(X) -> pre123
```
При `#__VA_OPT__(...)` сначала выполняется parameter substitution внутри
fragment, включая prescan обычных параметров, но произвольные macro names самого
fragment до stringification дополнительно не rescanning-ятся. Поэтому:
```text
#define X 123
#define S(a, ...) #__VA_OPT__(a X)
S(X, y) -> "123 X"
```
Если parameter внутри `__VA_OPT__` непосредственно участвует во внутреннем
`##`, для него, как обычно, prescan подавляется; paste выполняется до дальнейшего
rescan. Внешний `##`, соседний с `__VA_OPT__`, получает крайний token уже
подготовленного fragment. Пустой результат `__VA_OPT__` рядом с `##` ведёт себя
как placemarker.
`__VA_OPT__` допустим только в replacement list variadic function-like macro и
должен непосредственно задавать parenthesized fragment. `##` не может быть
первым или последним preprocessing token внутри самого `__VA_OPT__`.
Историческое GNU-расширение
```text
, ## __VA_ARGS__
```
в `mcpu-cpp` намеренно **не реализуется**. Для условной запятой следует
использовать современную форму `__VA_OPT__(,)`. Старое GNU-расширение с
именованным variadic parameter `args...` также остаётся неподдерживаемым.
### 7.5. Нормализация пробелов в replacement list
Начиная с 0.0.48 `mcpu-cpp` не переносит в результат разворачивания
служебное выравнивание многострочного macro. После удаления `\` + newline
последовательность пробельных символов, принадлежащая самому replacement list,
канонизируется в один ASCII-пробел. Это особенно важно для определений, где
обратные косые черты визуально выровнены в одну колонку:
```text
#define TRACE(x) \
do \
{ \
output(x); \
done(); \
} \
while( 0 )
```
При разворачивании такое определение выдаёт компактный replacement:
```text
do { output(x); done(); } while( 0 )
```
а не сохраняет десятки пробелов перед каждой бывшей границей физической
строки.
Нормализация относится **только к whitespace самого replacement list**.
`mcpu-cpp` не является formatter-ом исходной программы: пробелы в обычном
тексте input сохраняются. Пробелы внутри фактического macro argument также не
переформатируются только потому, что argument был подставлен в macro:
```text
#define ID(x) x
ID(a + b) -> a + b
```
Содержимое string/character literals сохраняется буквально, поэтому:
```text
#define S "left right"
```
по-прежнему содержит пять пробелов внутри строки.
Наличие whitespace между preprocessing tokens сохраняется как один пробел.
Это не позволяет случайно изменить tokenization, например превратить `+ +` в
`++`, `- >` в `->` или `< <` в `<<`. Операторы `#` и `##`, placemarkers,
`__VA_ARGS__`, `__VA_OPT__` и последующий rescan продолжают использовать свои
существующие правила; новая политика меняет только количество обычного
replacement-list whitespace.
Dump-режимы (`-dM`, `-dD`) показывают ту же каноническую форму replacement
list, которая хранится во внутренней таблице macro.
### 7.6. Компактификация невидимых строк и linemarkers
Начиная с 0.0.49 `mcpu-cpp` использует для вертикального whitespace ту же
модель, что GNU CPP: **удаляем, но не забываем**. Строки, которые после
preprocessing не породили ни одного выводимого preprocessing token, не обязаны
оставаться физическими пустыми строками в `.E`, однако их исходная позиция
продолжает учитываться при построении linemarkers и значении `__LINE__`.
Причина невидимости не имеет значения. Это могут быть удалённые directives,
неактивные ветви `#if`, однострочные и многострочные comments, обычные пустые
строки или их смесь. Emitter сравнивает текущую output source position с
позицией следующей реально выдаваемой строки.
Если следующая позиция находится менее чем через восемь строк, разрыв
представляется обычными newline. Если расстояние равно восьми строкам или
больше, вместо длинной последовательности пустых строк выдаётся корректирующий
linemarker:
```text
# N "file"
```
и следующая содержательная строка сразу относится к source line `N`. Таким
образом граница поведения совместима с GNU CPP: gaps 0..7 сохраняются через
newline, gap 8 и больше заменяется linemarker.
Structural markers входа и возврата из include-файла сохраняют обычный смысл:
```text
# 1 "header.h" 1
# 4 "source.c" 2
```
Если included file не породил никакого output, `mcpu-cpp` не создаёт
искусственный marker, сообщающий, до какой внутренней строки header дошёл
препроцессор. После enter-marker сразу может следовать return-marker. Реальная
позиция снова уточняется только тогда, когда требуется выдать следующий
содержательный текст.
Эта оптимизация меняет только представление output stream. Source coordinates,
`__LINE__`, diagnostics, `#line`, include enter/return semantics и обработка
macro остаются привязаны к исходному логическому потоку, а не к количеству
физических строк в сжатом `.E`.
## 8. Предопределённые макро
Начиная с 0.0.6 был перенесён исторический механизм predefined macros из
препроцессора. Этот механизм оформлен как отдельный ABI/environment layer
будущего безымянного C-подобного языка. Эти определения не являются
декоративными: их имена и значения должны соответствовать либо семантике GNU
CPP, либо явно документированному MCPU/LibMPU contract.
### 8.1. Динамические source macros
Следующие predefined macros вычисляются в точке использования:
| Макро | Раскрытие |
|---|---|
| `__FILE__` | строковая константа с именем текущего входного файла |
| `__LINE__` | десятичный номер текущей строки |
| `__BASE_FILE__` | строковая константа с именем главного входного файла translation unit |
| `__INCLUDE_LEVEL__` | уровень вложенности `#include`; для главного файла равен `0` |
| `__DATE__` | дата запуска препроцессора в форме `"Mmm dd yyyy"` |
| `__TIME__` | время запуска препроцессора в форме `"hh:mm:ss"` |
`__DATE__` и `__TIME__` получают один timestamp на весь
translation unit. Специальное раскрытие помещается в output без повторного macro
rescan.
Эти имена находятся в общей macro table, поэтому `#undef` и последующий
`#define` могут осознанно заменить builtin.
### 8.2. Версия препроцессора
Начиная с 0.0.8 standalone preprocessor не определяет GCC-имя `__VERSION__`.
Оно относится к compiler environment, которого для будущего high-level языка
пока нет. Собственная версия `mcpu-cpp` имеет отдельное однозначное имя:
```text
#define __MCPU_CPP_VERSION__ "1.0.3"
```
Значение автоматически берётся из `PACKAGE_VERSION`. Когда появится compiler
frontend/driver, его version contract будет определён отдельно и не будет
смешиваться с версией standalone preprocessor.
### 8.3. Источники истины ABI
`mcpu-cpp` собирается только GNU GCC. Во время `configure` проект использует
проверенные приёмы из `LibMPU`/`LibMPUIO` `acsite.m4`: GCC predefined macros
определяют native type sizes, byte/word order и machine-register width, а
установленный `<libmpu.h>` является окончательным источником настроек LibMPU.
В частности, фиксируются и проверяются:
```text
MPU_REAL_IO_LIMIT
MPU_MATH_FN_LIMIT
MPU_BYTE_ORDER
MPU_WORD_ORDER
BITS_PER_MACHINE_REGISTER
BITS_PER_UNIT_T
sizeof(__mpu_size_t)
sizeof(__mpu_ptrdiff_t)
```
`configure` дополнительно проверяет, что byte order и
`BITS_PER_MACHINE_REGISTER`, записанные в LibMPU, согласованы с GCC target,
которым собирается `mcpu-cpp`. `MPU_WORD_ORDER` берётся непосредственно из
configured LibMPU profile и описывает порядок слов MCPU data environment.
Пределы `MPU_REAL_IO_LIMIT` и `MPU_MATH_FN_LIMIT` имеют разные назначения.
Например, библиотека может иметь Real I/O до 65536 бит и математические функции
только до 16384 бит. Поэтому `MPU_MATH_FN_LIMIT` не используется как предел
существования типов Real.
### 8.4. MCPU architecture и assembler prefixes
Целевая архитектура определяется макро:
```text
#define _ARCH_MCPU 1
```
MCPU PTR64 имеет ширину 64 бита, поэтому определены `__SIZEOF_POINTER__`,
`__MCPU_POINTER_WIDTH__`, `__INTPTR_TYPE__`, `__UINTPTR_TYPE__`, соответствующие
width/max macros.
Смысл assembler-prefix macros согласован с GNU CPP, а не с первой буквой имени
register view. В синтаксисе `mcpu-as` дополнительного sigil перед register,
label или immediate нет. `r` и `c` являются частью MCPU register syntax, а не
`REGISTER_PREFIX`. Поэтому:
```text
#define __REGISTER_PREFIX__
#define __LOCAL_LABEL_PREFIX__
#define __USER_LABEL_PREFIX__
#define __IMMEDIATE_PREFIX__
```
все четыре раскрываются в пустую последовательность. `.L...` остаётся
compiler naming convention и не является assembler ABI local-label prefix:
LOCAL/GLOBAL binding определяется symbol directives.
### 8.5. Byte order и word order
Базовые числовые значения порядка байт совместимы с GNU CPP:
```text
__ORDER_LITTLE_ENDIAN__
__ORDER_BIG_ENDIAN__
__ORDER_PDP_ENDIAN__
```
Но целевая среда публикует собственные MCPU names:
```text
#define __MCPU_BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
#define __MCPU_WORD_ORDER__ __ORDER_LITTLE_ENDIAN__
#define __BYTE_ORDER__ __MCPU_BYTE_ORDER__
```
Фактические значения `__MCPU_BYTE_ORDER__` и `__MCPU_WORD_ORDER__` получают из
configured LibMPU profile (`MPU_BYTE_ORDER` и `MPU_WORD_ORDER`). Поэтому они
следуют host data representation, с которой собрана LibMPU. Это не меняет
отдельный архитектурный контракт кодировки MCPU instruction bytecode.
GNU/C-specific имя `__FLOAT_WORD_ORDER__` не определяется: типа `float` в
будущем языке MCPU нет.
Параметры LibMPU/MCPU environment публикуются в MCPU namespace:
```text
__MCPU_MACHINE_REGISTER_WIDTH__
__MCPU_REAL_IO_LIMIT__
__MCPU_MATH_FN_LIMIT__
__MCPU_INT_MAX_WIDTH__
__MCPU_REAL_MAX_WIDTH__
__MCPU_COMPLEX_MAX_WIDTH__
```
`__MCPU_INT_MAX_WIDTH__` равен `NB_I_MAX * 8`, а Real/Complex maximum width
равен configured `MPU_REAL_IO_LIMIT`. `__MCPU_MACHINE_REGISTER_WIDTH__` является
значением `BITS_PER_MACHINE_REGISTER` установленной LibMPU. Пределы Real I/O и
math functions не смешиваются: `MPU_REAL_IO_LIMIT` определяет существование
Real/Complex type family и text conversion, а `MPU_MATH_FN_LIMIT` — наличие
математических функций соответствующей ширины.
### 8.6. MCPU size/ssize, `ptrdiff` и pointers
Будущий язык не наследует variable-width C names `short`, `int`, `long` и
не использует C-style имя `size_t` как часть собственного ABI. Беззнаковый
LibMPU size type и знаковый byte-count/error type публикуются симметрично в
MCPU namespace. Например для 64-bit configured profile:
```text
#define __MCPU_SIZE_TYPE__ uint64
#define __MCPU_SIZE_WIDTH__ 64
#define __MCPU_SIZEOF_SIZE__ 8
#define __MCPU_SIZE_MAX__ 0xffffffffffffffff
#define __MCPU_SSIZE_TYPE__ int64
#define __MCPU_SSIZE_WIDTH__ 64
#define __MCPU_SIZEOF_SSIZE__ 8
#define __MCPU_SSIZE_MAX__ 0x7fffffffffffffff
```
Это MCPU-specific family, а не попытка приписать GNU CPP несуществующий
стандартный `__SSIZE_*` contract.
MCPU pointer ABI от host не зависит: PTR64 всегда имеет ширину 64 бита:
```text
#define __INTPTR_TYPE__ int64
#define __UINTPTR_TYPE__ uint64
#define __INTPTR_WIDTH__ 64
#define __UINTPTR_WIDTH__ 64
#define __INTPTR_MAX__ 0x7fffffffffffffff
#define __UINTPTR_MAX__ 0xffffffffffffffff
#define __SIZEOF_POINTER__ 8
#define __MCPU_POINTER_WIDTH__ 64
```
Разность MCPU pointers является знаковой и также фиксирована независимо от
host:
```text
#define __PTRDIFF_TYPE__ int64
#define __PTRDIFF_WIDTH__ 64
#define __SIZEOF_PTRDIFF__ 8
#define __PTRDIFF_MAX__ 0x7fffffffffffffff
```
Computed MIN expressions вроде `(-__PTRDIFF_MAX__ - 1)` в predefined table не
создаются.
### 8.7. Character types
Обычного C `char` в будущем языке нет. Поэтому `__CHAR_TYPE__` и
`__WCHAR_TYPE__` не определяются. Типы языка называются без C/C++ suffix `_t`:
```text
#define __CHAR8_TYPE__ char8
#define __CHAR16_TYPE__ char16
#define __CHAR8_WIDTH__ 8
#define __CHAR16_WIDTH__ 16
#define __SIZEOF_CHAR8__ 1
#define __SIZEOF_CHAR16__ 2
```
Это типы будущего языка. Внутренняя реализация самого `mcpu-cpp` по-прежнему
использует LibMPUIO `__mpu_char16_t` и strict UCS-2 text model.
### 8.8. Integer families LibMPU
Полная structural metadata integer families строится не по жёстко записанному
последнему типу, а до `NB_I_MAX * 8` фактически установленной LibMPU. Для
каждой power-of-two ширины от 8 бит определяются TYPE, WIDTH и SIZEOF:
```text
#define __INT1024_TYPE__ int1024
#define __UINT1024_TYPE__ uint1024
#define __INT1024_WIDTH__ 1024
#define __UINT1024_WIDTH__ 1024
#define __SIZEOF_INT1024__ 128
#define __SIZEOF_UINT1024__ 128
```
На текущей LibMPU 1.0.35 `NB_I_MAX == 8192`, поэтому family доходит до
`int65536`/`uint65536`, а `__SIZEOF_INT65536__ == 8192`.
Decimal-digit metadata определяется для **каждой** разрешённой integer width:
```text
__INT<bits>_DECIMAL_DIG__
__UINT<bits>_DECIMAL_DIG__
```
Значение вычисляется собственными integer-only helpers `mcpu-cpp` из известной
ширины типа. Оно означает точное число десятичных цифр максимального значения
соответствующего типа: знак и завершающий NUL в `DECIMAL_DIG` не входят. Для
unsigned используется максимум `2^bits - 1`, для signed — `2^(bits-1) - 1`.
Это отличается от LibMPU `_int_digs()`, которая предназначена для оценки
строкового буфера и включает место для завершающего NUL.
Например:
```text
#define __INT64_DECIMAL_DIG__ 19
#define __UINT64_DECIMAL_DIG__ 20
#define __INT256_DECIMAL_DIG__ 77
#define __UINT256_DECIMAL_DIG__ 78
```
Только сами textual maxima намеренно ограничены шириной `bits <= 256`:
```text
__INT128_MAX__
__UINT128_MAX__
```
Максимумы строятся через LibMPU `iuitoa()`. Макро `__INT<bits>_MIN__` не
создаются: predefined table не должна содержать вычисляемые выражения вида
`(-__INT<bits>_MAX__ - 1)`. Для widths больше 256 бит отсутствуют только MAX;
TYPE/WIDTH/SIZEOF/DECIMAL_DIG сохраняются до полного `NB_I_MAX * 8`.
### 8.9. Real и Complex families LibMPU
Real/Complex structural metadata генерируется для каждой power-of-two ширины от
32 бит до фактического configured `MPU_REAL_IO_LIMIT`. Для всех этих типов
публикуются TYPE, WIDTH и SIZEOF.
Для Complex WIDTH означает параметр типа, а не суммарную storage width:
```text
#define __COMPLEX128_TYPE__ complex128
#define __COMPLEX128_WIDTH__ 128
#define __SIZEOF_COMPLEX128__ 32
```
`complex128` состоит из двух компонентов `real128`, поэтому его storage size
равен 32 байтам. При `MPU_REAL_IO_LIMIT == 65536` верх family имеет вид:
```text
#define __COMPLEX65536_TYPE__ complex65536
#define __COMPLEX65536_WIDTH__ 65536
#define __SIZEOF_COMPLEX65536__ 16384
```
Для Real соответственно:
```text
#define __REAL65536_TYPE__ real65536
#define __REAL65536_WIDTH__ 65536
#define __SIZEOF_REAL65536__ 8192
```
Precision metadata определяется для **всех** разрешённых Real widths вплоть
до `MPU_REAL_IO_LIMIT`. Имена macros согласованы с LibMPU helpers:
```text
__REAL<bits>_DECIMAL_DIG__ -> _real_digs(bits/8)
__REAL<bits>_MANT_DIG__ -> _real_mant_digs(bits/8)
```
`__REAL<bits>_DIG__` намеренно отсутствует. Ограничение `bits <= 256` относится
только к большим textual numeric constants. Для размеров до 256 бит также
определяются:
```text
__REAL<bits>_MAX__
__REAL<bits>_MIN__
__REAL<bits>_EPSILON__
__REAL<bits>_MAX_EXP__
__REAL<bits>_MIN_EXP__
__REAL<bits>_MAX_10_EXP__
__REAL<bits>_MIN_10_EXP__
```
Например, на LibMPU 1.0.35 для `real128` текущий profile даёт значения вида:
```text
#define __REAL128_EPSILON__ 2.524354896707237777317531409e-29
#define __REAL128_MAX__ 4.197157432934775384808581951e+323228496
#define __REAL128_MIN__ 9.530259619551804292864984035e-323228497
#define __REAL128_MAX_10_EXP__ 323228496
#define __REAL128_MAX_EXP__ 1073741823
#define __REAL128_MIN_10_EXP__ -323228524
#define __REAL128_MIN_EXP__ -1073741822
```
MAX/MIN/EPSILON создаются самой LibMPU и преобразуются через
`real_to_ascii()`. Exponent constants получают значения через LibMPU exponent
helpers и integer conversion. Для widths больше 256 бит эти numeric predefines отсутствуют, но
TYPE/WIDTH/SIZEOF/DECIMAL_DIG/MANT_DIG продолжаются до `MPU_REAL_IO_LIMIT`.
Для каждого разрешённого Real type вплоть до `MPU_REAL_IO_LIMIT` также
публикуются две компактные характеристики:
```text
#define __SIZEOF_REAL128_EXP__ 4
#define __REAL128_MAX_STRLEN__ 60
```
`__SIZEOF_REALxxx_EXP__` непосредственно получает `_sizeof_exp(NB_Rxxx)`.
`__REALxxx_MAX_STRLEN__` получает `_real_max_string(NB_Rxxx)` и означает
максимальное **количество символов** текстового представления, а не количество
байт. Поэтому для zero-terminated строки нужно резервировать не менее
`__REALxxx_MAX_STRLEN__ + 1` элементов: для `char8` это столько же bytes, а для
`char16` физический объём в bytes вдвое больше. Эти два metadata-macro
определяются и для Real widths больше 256, поскольку сами их значения малы.
### 8.10. Dump macros: `-dM`, `-dMP`
Опция:
```text
mcpu-cpp -dM input.c
```
печатает только итоговые **непредопределённые** macros в форме `#define ...`.
К этой группе относятся определения из основного файла и включённых headers, а
также определения командной строки `-D`. Предопределённые macros самого
MCPU-CPP в `-dM` не выводятся. Поэтому `-dM` предназначен прежде всего для
короткой инспекции macro-state, созданного пользовательской программой.
Опция:
```text
mcpu-cpp -dMP input.c
```
добавляет к тому же итоговому состоянию активные predefined macros MCPU-CPP.
Вывод имеет две последовательные группы: сначала все predefined macros, затем
все непредопределённые macros. Внутри каждой группы определения
детерминированно сортируются по имени. Такое разделение удобно системному
разработчику для инспекции preprocessing ABI и архитектурных свойств текущей
MCPU environment, не смешивая их с пользовательскими определениями.
Принадлежность к группе определяется происхождением macro, а не его именем.
Macro, заданный через `-D` или `#define`, является обычным даже если его имя
похоже на системное. Если predefined macro был удалён через `#undef`, он не
печатается. Если после этого то же имя снова определено пользователем, новое
определение относится к обычной группе и выводится в её части `-dMP`, а также
в `-dM`. Тем самым оба режима показывают именно **итоговый macro-state**.
Context-dependent `__FILE__`, `__LINE__`, `__DATE__`, `__TIME__`,
`__BASE_FILE__` и `__INCLUDE_LEVEL__` в статическом dump не печатаются.
Статические ABI/architecture predefined macros и вычисляемые static Real
metadata выводятся в `-dMP`.
Если input file указан, он сначала полностью препроцессируется, после чего
выводится итоговый macro-state; обычный preprocessed text в режимах `-dM` и
`-dMP` не выдаётся. Без input file используется stdin, поэтому пустой stdin с
`-dM` даёт пустой dump, а `-dMP` позволяет получить набор активных static
predefined macros текущей MCPU environment.
`-dD` имеет другую семантику и этим разделением не затрагивается.
### 8.11. Dump definitions: `-dD`
Опция:
```text
mcpu-cpp -dD input.c
```
сохраняет обычный результат препроцессирования и одновременно выводит
встреченные директивы `#define`. Перед началом основного входного текста
печатаются статические предопределённые macro definitions. Каждой такой
дефиниции предшествует marker:
```text
# 0 "<built-in>"
#define NAME value
```
а перед блоком предопределённых macro выводится marker исходного файла вида
`# 0 "input.c"`. Context-dependent `__FILE__`, `__LINE__`, `__DATE__`,
`__TIME__`, `__BASE_FILE__` и `__INCLUDE_LEVEL__` в начальный built-in block
не включаются.
### 8.12. Dump configuration: `-dconfig`
Опция:
```text
mcpu-cpp -dconfig
```
не требует input file и выводит effective variables configuration layer после чтения runtime config, необязательного system override, домашнего
user override или выбранного `--config-file`, включая expansion
`$NAME`/`${NAME}`. При `--sys-root=PATH` configuration files не читаются, а
dump показывает выбранный из командной строки system root `PATH/include`. Строки сортируются по имени и печатаются в форме:
```text
NAME = value;
```
Это позволяет проверить реальные include paths без ручного поиска
`<runtime-root>/etc/mcpu-cpp.conf`, `/etc/mcpu/mcpu-cpp.conf` и
`$HOME/.mcpu/etc/mcpu-cpp.conf`.
### 8.13. Verbose configuration snapshot: `-v`
При `-v` MCPU-CPP сохраняет прежний runtime trace для `#lang`, `#include` и
`#include_next`, но конфигурационные переменные печатаются только один раз —
после чтения всех уровней configuration и применения правил приоритета. Поэтому
в verbose output видны только **effective values**, а промежуточные значения из
runtime-root, system и user config не дублируются.
Config-блок выводится в порядке include policy: language-specific user paths,
общий user path, system root и AFTER path. Переменная, отсутствующая во всех
уровнях configuration, не печатается. Runtime-derived default
`MCPU_CPP_SYSTEM_INCLUDE_PATH` является полноценным самым нижним значением и
поэтому виден при `-v`, даже если ни один `mcpu-cpp.conf` не найден **или все
config-файлы отключены опцией `--no-config`**.
Форма строки:
```text
config: NAME=value
```
### 8.14. Effective search directories: `-dsearch-dirs`
Опция:
```text
mcpu-cpp -dsearch-dirs
```
не требует input file, печатает effective глобальные каталоги поиска и
завершает работу без preprocessing. Формат намеренно прост:
```text
search: /path/to/directory
```
Каталоги выводятся в семантическом порядке классов поиска:
```text
explicit -I
explicit -isystem
configured language-specific user directories
MCPU_CPP_INCLUDE_PATH
MCPU_CPP_SYSTEM_INCLUDE_PATH/<lang>
MCPU_CPP_SYSTEM_INCLUDE_PATH
explicit -idirafter
MCPU_CPP_AFTER_INCLUDE_PATH
```
Language-specific entries печатаются для всех поддерживаемых языков в их
каноническом порядке. Во время реального `#include` из этой группы участвует
только каталог активного `#lang`. Каталог текущего физического файла в
`-dsearch-dirs` не выводится: он существует только динамически для конкретного
`#include "..."` и меняется вместе с include stack. `--no-config` не удаляет
runtime-derived system root, поэтому без конфигурационных файлов dump всё равно
содержит `<runtime-root>/include/<lang>` и `<runtime-root>/include`. `-nostdinc` удаляет
из dump effective system `<lang>` entries и system root, но не explicit
`-isystem`. Не существующий на filesystem каталог всё равно показывается,
поскольку он является элементом effective search configuration и просто будет
пропущен при реальном поиске файла.
`-dsearch-dirs` учитывает `-I`, `-isystem`, `-idirafter`, все уровни config и
replacement-семантику `MCPU_CPP_SYSTEM_INCLUDE_PATH`. Опция `-o` вместе с ним
является ошибкой.
### 8.15. Условная компиляция
Директивы `#if`, `#ifdef`, `#ifndef`, `#elif`, `#else` и `#endif` обрабатываются
как управляющие директивы препроцессора и в выходной поток не копируются, в
том числе при `-dD`. Неактивные ветви пропускаются без выполнения находящихся
в них `#define`, `#undef` и `#include`; вложенные условные группы при этом
учитываются корректно.
Выражение `#if` сначала обрабатывает оператор
`defined`, затем выполняется macro expansion, а оставшиеся идентификаторы
имеют значение `0`. Поддерживаются арифметические, битовые, сравнительные и
логические операции, `?:` и short-circuit semantics для `&&`, `||` и `?:`.
Начиная с 0.0.26 синтаксис выражения разбирается parser-ом, генерируемым
ZUBR 4.1.0 из `src/mcpp-expr.zubr`; в том же файле находится UCS-2 lexical
analyzer. Предварительная обработка `defined` и macro expansion выполняются до
входа в parser. Арифметическая семантика вынесена в `mcpp-semantic.c/h` и не
зависит от размеров целых типов host-системы. Generated `mcpp-expr.c`
включается в release, поэтому ZUBR требуется только при изменении grammar.
#### 8.15.1. Единственная вычислительная разрядность — 64 бита
MCPU-CPP является препроцессором, а не компилятором языка общего назначения.
Все целочисленные вычисления в директивах условной компиляции выполняются
только в 64-разрядной арифметике. Препроцессор не выполняет арифметику LibMPU
произвольной разрядности, вещественные или комплексные вычисления.
Если программисту не требуется управлять двоичным представлением литерала,
достаточно обычных целых констант и необязательного `U`/`u`. Например:
```c
#if 2 > 1
#if 0xffffffffffffffffU > 1
```
Числовой lexeme хранится в UCS-2 до классификации, после чего его ASCII-часть
передаётся LibMPU `iatoui()`. Поддерживаются `0b...`, `0...`, decimal и
`0x...`. Значение, не помещающееся в 64 бита, является ошибкой. Старые C
suffixes `L`, `l`, `LL`, `ll` не поддерживаются.
#### 8.15.2. Суффикс разрядности `zNNN[Uu]`
MCPU-CPP понимает общий для MCPU-языков суффикс разрядности:
```text
zNNN
ZNNN
zNNNu
zNNNU
ZNNNu
ZNNNU
```
`NNN` — непустая последовательность десятичных цифр и **всегда** читается как
десятичное число, даже если начинается с нулей. Поэтому `z8`, `z08` и `z008`
задают одну и ту же разрядность 8 бит.
В общем синтаксисе MCPU корректная разрядность должна быть степенью двойки от
8 до `MPU_REAL_IO_LIMIT`. MCPU-CPP, однако, сознательно ограничен 64-битными
вычислениями:
* `z8`, `z16`, `z32`, `z64` и варианты регистра допустимы;
* значение `NNN > 64` немедленно является ошибкой: препроцессор не допускает
числовые константы разрядности выше 64 бит в директивах условной компиляции;
* если `NNN <= 64`, но не задаёт допустимую степень двойки, например `z24`,
выводится warning и сам `zNNN` игнорируется;
* необязательный следующий `U`/`u` задаёт unsigned и сохраняет своё значение
даже если некорректный `zNNN` был проигнорирован.
После полного суффикса должна заканчиваться числовая preprocessing token.
Оператор или punctuation начинает следующий token, поэтому допустимы
`1z32u+2`, `(1z32u)` и `1z32u==1`. Записи вроде `1z32undefined`, `1z32ufoo` и
`1z32$foo` являются ошибками и не разбиваются искусственно на число и имя.
#### 8.15.3. Нормализация литерала
Суффикс разрядности действует **только один раз — при формировании значения
самой константы**. Разрядность не сохраняется в semantic value и не участвует
в последующих операциях.
Для `VALUEzNNN` значение считается знаковым N-битным числом в дополнительном
коде:
1. сохраняются младшие `NNN` бит;
2. результат расширяется со знаком до 64 бит.
Для `VALUEzNNNu`/`VALUEzNNNU` сохраняются младшие `NNN` бит, после чего
выполняется нулевое расширение до 64 бит.
Например:
```text
0x7fz8 -> 0x000000000000007f -> 127
0x80z8 -> 0xffffffffffffff80 -> -128
0xffz8 -> 0xffffffffffffffff -> -1
0x80z8u -> 0x0000000000000080 -> 128
0xffz8u -> 0x00000000000000ff -> 255
0x1ffz8 -> 0xffffffffffffffff -> -1
0x1ffz8u -> 0x00000000000000ff -> 255
```
Последние два примера намеренны: `zNNN` задаёт разрядность **двоичного
представления**, а не проверку математического диапазона. Биты старше N
отбрасываются до расширения.
После этой нормализации никакой `z8`, `z16` или `z32` в вычислительной модели
уже не существует. Внутреннее значение содержит только 64-битный битовый
образ и признак signed/unsigned.
#### 8.15.4. Все последующие операции — 64-битные
После нормализации все арифметические, побитовые, сравнительные и логические
операции выполняются над 64-битными операндами. Результат операции не
усекается обратно до разрядности исходного suffix. Поэтому:
```text
0x7fz8 + 1 -> 128
0xffz8u + 1 -> 256
```
а не `-128` и `0` соответственно. Аналогично `~0xffz8u` инвертирует все 64
бита и даёт `0xffffffffffffff00`.
Для бинарных операций, где signedness имеет значение, наличие unsigned
операнда переводит операцию в 64-битную unsigned-интерпретацию. Сравнения
возвращают `0` или `1`. Логические `!`, `&&`, `||` также возвращают signed
64-битные `0` или `1`; short-circuit не вычисляет невыбранную часть.
Сдвиги выполняются после 64-битной нормализации. Правый сдвиг signed
отрицательного значения является арифметическим, unsigned — логическим.
Например:
```text
0x80z8 >> 1 -> -64
0x80z8u >> 1 -> 64
```
Историческое правило MCPU-CPP для отрицательного счётчика сдвига сохраняется:
`A << -N` эквивалентно `A >> N`, а `A >> -N` — `A << N`.
Таким образом, `zNNN` не превращает препроцессор в компилятор с системой
integer promotions разных размеров. Он лишь позволяет явно описать битовый
образ исходного литерала; затем выражение вычисляется в единственной простой
64-битной модели.
#### 8.15.5. Символьные константы
Символьная единица имеет тип `__mpu_uint16_t`, соответствующий внутреннему
UCS-2 представлению, и перед вычислением расширяется нулями до 64 бит.
Последующая арифметика снова является обычной 64-битной арифметикой.
Состояние условной компиляции хранится в отдельном стеке; условная группа не
может пересекать границу include-файла.
### 8.16. Диагностические директивы `#error` и `#warning`
MCPU-CPP поддерживает стандартные диагностические директивы:
```text
#error сообщение
#warning сообщение
```
`#error` выдаёт diagnostic уровня error с текущими логическими именем файла и
номером строки и немедленно завершает preprocessing с ошибкой. `#warning`
выдаёт warning с той же source-location information, после чего preprocessing
продолжается. Поэтому предшествующий `#line` влияет на координаты обеих
диагностик.
Остаток строки после имени директивы
**не подвергается macro expansion**. Например:
```c
#define MESSAGE expanded
#warning MESSAGE
```
печатает `MESSAGE`, а не `expanded`. Это отличает диагностические директивы от
`#if` и `#line`, где macro expansion является частью соответствующего
контракта.
Комментарии удаляются на обычной preprocessing phase до обработки директивы.
Начальные и конечные пробелы сообщения удаляются, последовательности пробельных
символов между preprocessing tokens сворачиваются в один пробел. Пробелы внутри
кавычек сохраняются. Например:
```c
#warning one /* comment */ two
#warning "a b"
```
дают сообщения соответственно `one two` и `"a b"`. Unicode-текст проходит
через внутреннее UCS-2 представление и выводится во внешнюю диагностику в UTF-8.
Обе директивы являются управляющими и никогда не копируются в обычный выходной
поток или в `-dD`. В неактивной ветви `#if` они полностью игнорируются, поэтому
обычная защитная конструкция работает ожидаемо:
```c
#if 0
#error this error is inactive
#endif
```
### 8.17. Управление предупреждениями: `-Wcomment`, `-Wall`, `-Werror`
MCPU-CPP разделяет обязательные предупреждения, являющиеся частью уже
зафиксированной preprocessing-семантики, и дополнительные классы предупреждений,
которые включаются пользователем. Управление предупреждениями не изменяет
семантику `-dD`, macro expansion, conditional compilation или include search.
Опции `-Wcomment` и `-Wcomments` являются полными синонимами и включают два
лексических предупреждения:
* последовательность `/*`, встретившуюся внутри уже открытого `/* ... */`
комментария;
* backslash-newline внутри `//` комментария, из-за которого однострочный
комментарий физически продолжается на следующую строку.
По умолчанию этот дополнительный класс выключен. `-Wall` включает все
дополнительные warning classes MCPU-CPP; в версии 0.0.40 таким классом является
`-Wcomment`. Формы `-Wno-comment` и `-Wno-comments` выключают его. Как в GNU
warning model, более специфическая настройка имеет приоритет над групповой
независимо от порядка аргументов. Поэтому обе команды:
```text
mcpu-cpp -Wall -Wno-comment file.c
mcpu-cpp -Wno-comment -Wall file.c
```
оставляют comment warnings выключенными. Между настройками одинаковой
специфичности действует последнее указание, например `-Wno-comment -Wcomment`
включает этот класс.
`-Werror` не включает никаких новых warning classes. Он повышает до error любое
предупреждение, которое в данном запуске действительно было бы выдано, и такой
запуск завершается неуспешно. Это относится как к дополнительным comment
warnings, так и к уже существующим обязательным предупреждениям MCPU-CPP:
* активной директиве `#warning`;
* недопустимой, но не превышающей 64 бита ширине `zNNN`;
* переопределению macro другим replacement list;
* результату `##`, не образующему один preprocessing token.
Например:
```text
mcpu-cpp -Wcomment -Werror file.c
```
превращает найденный comment warning в error. В то же время один `-Werror` без
`-Wcomment`/`-Wall` не заставляет MCPU-CPP искать optional comment warnings.
`-Wno-error` возвращает обычную severity warning. Для `-Werror` и `-Wno-error`,
имеющих одинаковую специфичность, действует последняя опция командной строки.
Так, `-Werror -Wno-error` оставляет warnings предупреждениями, а
`-Wno-error -Werror` снова повышает их до errors.
В 0.0.40 намеренно не вводятся `-Werror=<class>`, `-Wno-error=<class>`,
`-Wundef`, `-Wunused-macros`, `-Wtraditional` и другие компиляторные классы.
Warning interface MCPU-CPP остаётся компактным и расширяется только тогда, когда
новый класс действительно нужен самому preprocessing language.
### 8.18. Идентификаторы UCS-2
Начиная с 0.0.22 имена preprocessing identifiers больше не ограничены ASCII.
Внутри `mcpu-cpp` текст уже представлен строгим UCS-2, а классификация символов
выполняется locale-independent функциями LibMPUIO 1.0.4, построенными по Unicode
18.0.0. Первый символ идентификатора должен быть `_` или иметь свойство
`XID_Start`; последующие символы должны быть `_`, `$` или иметь свойство
`XID_Continue`. Символ `$` является расширением `mcpu-cpp`: он разрешён только
после первого символа и не может начинать identifier. Это правило едино для
имён и параметров macro, `#undef`, `#ifdef`/`#ifndef`, `defined`, обычного macro
expansion, `#`/`##`. Имена остаются case-sensitive. Surrogate code units
`U+D800..U+DFFF` не являются допустимыми символами identifiers.
Например, допустимы:
```c
#define АНДРЕЙ 1
#define résumé 2
#define ΩМЕГА 3
#define VALUE$OLD 4
```
Например, `VALUE$OLD` допустим, а `$VALUE` недопустим, поскольку `$` не является
identifier-start character.
Combining marks и не-ASCII decimal digits могут входить в identifier в позициях
`XID_Continue`, но не становятся автоматически допустимыми первыми символами.
Синтаксис числовых констант от этого не меняется: его правила остаются правилами
соответствующего языка, а не Unicode `isdigit`.
### 8.19. Макросы командной строки `-D` и `-U`
Начиная с 0.0.23 опции `-D` и `-U` являются полноценными действиями
препроцессора. Поддерживаются формы:
```text
-DNAME
-DNAME=VALUE
-D'FUNC(a,b)=a+b'
-UNAME
```
`-DNAME` эквивалентна `#define NAME 1`; наличие `=` с пустой правой частью
задаёт пустой replacement list. Function-like определения используют тот же
macro engine, что и обычный `#define`, включая параметры, `#`, `##` и
последующий rescanning. `-U` использует тот же identifier contract, что и
`#undef`. Действия `-D`/`-U` выполняются в порядке командной строки после
установки predefined macros.
Только payload опций `-D` и `-U` интерпретируется как UTF-8 и преобразуется в
строгий UCS-2. Имена файлов, `-I`, другие pathname arguments и остальные
аргументы командной строки остаются исходными byte strings и не подвергаются
Unicode-конвертации.
Начиная с 0.0.25 символ `$` разрешён внутри имени macro, но не в первой
позиции. При передаче `$` из shell пользователь обязан учитывать правила самого
shell: shell обрабатывает `$` **до запуска `mcpu-cpp`**. Одинарные кавычки уже
полностью защищают `$`, например:
```sh
mcpu-cpp '-DАНДРЕЙ$_Y=62' input.c
```
Без кавычек `$` следует экранировать:
```sh
mcpu-cpp -DАНДРЕЙ\$_Y=62 input.c
```
или использовать двойные кавычки с экранированием:
```sh
mcpu-cpp -D"АНДРЕЙ\$_Y=62" input.c
```
Вариант без защиты:
```sh
mcpu-cpp -DАНДРЕЙ$_Y=62 input.c
```
не передаёт написанное имя буквально: `$...` сначала раскрывается shell и
`mcpu-cpp` получает уже изменённый `argv`. Внутри одинарных кавычек обратная
косая черта перед `$` не нужна и стала бы обычным символом аргумента.
Для command-line `-D` левая часть до первого `=` разбирается как отдельный
macro declarator. Если после допустимого имени (или завершённого списка
параметров function-like macro) до `=` встречается недопустимый хвост, этот
хвост молча отбрасывается и **никогда не превращается в replacement list**.
Например:
```text
-D'АНДРЕЙ@XYZ=62'
```
эквивалентно:
```c
#define АНДРЕЙ 62
```
а не ошибочной форме `#define АНДРЕЙ @XYZ 62`. Аналогичное правило допустимого
identifier-prefix применяется к `-U`. Если же первый символ вообще не является
допустимым identifier-start character (например `$` или цифра), определение
остаётся ошибочным.
При `-dD` определения, пришедшие через `-D`, маркируются отдельно от
предопределённых macro:
```text
# 0 "<command-line>"
#define NAME value
```
в то время как predefined macros продолжают использовать `<built-in>`.
### 8.20. Публичный интерфейс командной строки
`mcpu-cpp` поддерживает только актуальные опции, описанные `--help`. Устаревшие
compatibility-флаги не образуют скрытый интерфейс и диагностируются как
`unknown option`. Опция `-E` является исключением: она молча принимается и
игнорируется, поскольку может передаваться compiler driver при запуске
отдельного препроцессора.
Опция `--object-suffix SUFFIX` задаёт суффикс object target, используемый при
генерации make-зависимостей; аргумент обязателен.
## 9. Build-system и генераторы
Собственные Autoconf-макросы проекта находятся в корневом `acsite.m4`.
Каталог `m4/` зарезервирован для внешних/vendor M4-файлов. Такой порядок
повторяет принятую в библиотеках MCPU схему и не смешивает собственный
configure-код с импортированными макросами.
Парсер выражений `#if` генерируется ZUBR 4.1.0 из `src/mcpp-expr.zubr`.
Release archive содержит и грамматику, и уже сгенерированный `src/mcpp-expr.c`,
поэтому обычная сборка не требует установленного ZUBR. После изменения
грамматики developer build использует штатное правило Automake:
```text
zubr -vl -s -Bmcpp_ -o mcpp-expr.c mcpp-expr.zubr
```
Перед выпуском release generated C должен соответствовать грамматике, полный
test suite и `make distcheck` должны проходить без ошибок.
### 9.1. Developer bootstrap и Git source tree
Начиная с 0.0.50 корневой скрипт `./bootstrap` позволяет не хранить в Git файлы,
которые полностью воспроизводятся из исходников. Скрипт сначала генерирует
`src/mcpp-expr.c` из `src/mcpp-expr.zubr` с помощью ZUBR 4.1.0, затем выполняет
`aclocal`, `autoheader`, `automake` и `autoconf` в стиле библиотек LibMPU и
LibMPUIO. Опция `--target-dest-dir=DIR` задаёт target ROOTFS для системных
Autoconf macro/include directories.
Это правило относится именно к developer Git tree. **Release archive остаётся
самодостаточным**, как и раньше: он содержит `configure`, `Makefile.in`, helper
scripts Automake и уже сгенерированный `src/mcpp-expr.c`, поэтому обычная сборка
релиза не требует предварительного запуска `bootstrap` и не требует ZUBR.
Корневой `.gitignore` перечисляет воспроизводимые bootstrap-файлы и обычный
configure/build state. Он не меняет существующую release/build model, а только
позволяет поддерживать более чистый Git repository.
## 10. GNU-compatible features
`mcpu-cpp` является самостоятельным препроцессором MCPU, но для ряда хорошо
известных операций намеренно повторяет поведение GNU CPP. Совместимость
относится к документированным возможностям, а не означает полную CLI- или
языковую взаимозаменяемость с GCC.
В частности, GNU-compatible поведение используется для:
* object-like и function-like macro, повторного macro rescan, `#` и `##`;
* variadic macro `...` / `__VA_ARGS__` и стандартного `__VA_OPT__`;
* `#if`, `#ifdef`, `#ifndef`, `#elif`, `#else`, `#endif` и `defined`;
* `#include`, `#include_next`, `#pragma once`, `#line` и GNU linemarkers;
* compact output mapping: до семи невидимых строк представляются newline, а
разрыв в восемь и более строк — корректирующим linemarker;
* forced files `-include` / `-imacros` и dependency options `-M`, `-MM`, `-MD`,
`-MMD`, `-MF`, `-MT`, `-MQ`, `-MG`;
* warning controls `-w`, `-Wall`, `-Werror` и поддерживаемых `-Wcomment` forms.
MCPU-specific возможности, включая `#lang` / `#endlang`, числовой суффикс
`zNNN` и ABI predefined macros, остаются собственными расширениями `mcpu-cpp`.
|