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
|
mcpu-cpp
========
mcpu-cpp is the preprocessor/front-end source manager for MCPU programming
languages. It is an independent component of the LibMPU/LibMPUIO/LibMCPU
software platform and provides preprocessing, language selection, include-path
management, dependency generation and preprocessing diagnostics for the MCPU
toolchain.
Text model
----------
External text files are UTF-8. Internally mcpu-cpp uses strict UCS-2 through
LibMPUIO. UTF-8 scalar values which cannot be represented by UCS-2 are
rejected. Legacy external code pages are not supported; the external representation is UTF-8.
Lexical analysis
----------------
mcpu-cpp does not use flex. Its preprocessing scanners are hand-written and
operate on the internal UCS-2 text model. The `#if` expression grammar and its
expression lexer are generated by ZUBR 4.1.0.
Parser architecture
-------------------
The generated `src/mcpp-expr.c` is shipped in release archives, so ordinary
builds do not require ZUBR. In the developer Git tree the generated parser
may be omitted; the root `./bootstrap` script regenerates it from
`src/mcpp-expr.zubr` with ZUBR 4.1.0 before regenerating the Autotools files.
Documentation
-------------
The normative preprocessing manual is maintained in two synchronized forms:
doc/mcpu-cpp-en.md English
doc/mcpu-cpp-ru.md Russian
Both documents describe the same public contract and are distributed together.
Configuration
-------------
mcpu-cpp reads UTF-8 configuration files using a simple `NAME = value;`
syntax. Shell-style `$NAME` and `${NAME}` references are expanded from values
already defined in the effective configuration and then from the process
environment.
MCPU uses one relocatable installation tree rather than a versioned directory
per tool. With `--prefix=/usr --libdir=/usr/lib64`, `make install` creates:
```text
/usr/lib64/mcpu/
bin/mcpu-cpp
etc/mcpu-cpp.conf
include/
lib/
```
`/usr/bin/mcpu-cpp` is a public symbolic link to
`../lib64/mcpu/bin/mcpu-cpp`. The absolute `/usr/lib64/mcpu` path is an
install-time choice only; it is not embedded as the runtime MCPU root.
On Linux, mcpu-cpp resolves its real executable through `/proc/self/exe`, takes
the parent of the executable directory as the MCPU runtime root, and derives
`<root>/etc/mcpu-cpp.conf` and `<root>/include` from it. A fallback based on
`argv[0]`, `PATH`, and `realpath(3)` is used only when `/proc/self/exe` cannot
be read. Consequently a complete MCPU tree may be copied or moved without
rebuilding mcpu-cpp.
This is the intended ecosystem-wide layout for future `mcpu-as`, `mcpu-ld`,
`mcpu-run`, libraries and CRT components as well: tool versions do not define
separate roots; a coherent MCPU environment is identified by one physical
runtime tree.
Configuration layers are applied in this order:
```text
runtime-derived defaults
<runtime-root>/etc/mcpu-cpp.conf
/etc/mcpu/mcpu-cpp.conf optional
$HOME/.mcpu/etc/mcpu-cpp.conf optional, highest priority
```
The packaged config deliberately does not store an absolute default system
include path. Before reading any config file mcpu-cpp sets
`MCPU_CPP_SYSTEM_INCLUDE_PATH=<runtime-root>/include`; any higher-priority
configuration may replace that value or set it empty.
`--config-file FILE` reads only FILE on top of the runtime-derived defaults.
`--no-config` reads no configuration files at all but keeps those runtime
defaults. `--sys-root=PATH` uses `PATH` as the MCPU root, sets the effective
system include tree to `PATH/include`, and implies `--no-config`. `PATH` may
be absolute or relative; a relative path is resolved from the invocation
working directory. Use `-nostdinc` when the effective standard-system include
tree itself must be suppressed for one invocation.
Installation does not create `/etc/mcpu`; that directory is reserved for an
optional distributor or system-administrator override. The per-user
configuration is deliberately not versioned.
Recognized path variables are:
MCPU_CPP_INCLUDE_PATH
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_SYSTEM_INCLUDE_PATH
MCPU_CPP_AFTER_INCLUDE_PATH
User and AFTER path lists use the host PATH separator (`:` on UNIX systems).
`MCPU_CPP_SYSTEM_INCLUDE_PATH` is different: it is one replaceable root of the
MCPU system-header tree. Its runtime-derived default is
`<runtime-root>/include`. When a switched language is active mcpu-cpp
searches `<root>/<lang>` and then `<root>`. A higher-priority configuration can
replace the root completely for a developer/tester sandbox, or set it to an
empty value to disable the configured system tree.
Command line
------------
mcpu-cpp [options] [input [output]]
Important options:
-o FILE write output to FILE instead of stdout
-D NAME[=VALUE] define a command-line macro
-U NAME undefine a command-line macro
-imacros FILE preprocess FILE for macro state; discard its output
-include FILE preprocess FILE before the primary input
-I DIR, -IDIR add a user include directory
-isystem DIR add an explicit system include directory
-idirafter DIR add a directory searched after system directories
-nostdinc suppress the effective standard-system include tree
-dM dump non-predefined macros to stdout
-dMP dump predefined macros first, then other macros
-dD preserve #define directives in normal output
-dconfig dump effective configuration variables to stdout
-dsearch-dirs dump effective include search directories and exit
-M output make dependencies including system headers
-MM output make dependencies excluding system headers
-MD write dependencies and keep preprocessing output
-MMD like -MD but exclude system headers
-MF FILE write dependencies to FILE ('-' means stdout)
-MT TARGET set unquoted make dependency target
-MQ TARGET set make-quoted dependency target
--object-suffix SFX set object suffix used for dependency targets
-w suppress all warnings
-Wcomment[s] warn about nested /* and multi-line // comments
-Wno-comment[s] disable comment warnings even under -Wall
-Wall enable all optional warning classes
-Werror promote every emitted warning to an error
-Wno-error keep emitted warnings as warnings
--config-file FILE use only FILE as the configuration file
--no-config do not read config files; keep runtime-derived defaults
--sys-root=PATH use PATH as MCPU root and do not read config files
-v, --verbose print configuration and include activity
--help print help
--version print version
Include search
--------------
For `#include "file"`, the physical directory containing the current source
file is searched first. For `#include <file>`, that first step is omitted.
The remaining include search order is normative:
```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
```
Command-line include directories therefore override persistent configuration.
The language-specific user paths are freely configurable. The system path is
a single effective root; mcpu-cpp derives the fixed `<root>/<lang>` directory
itself, so there are intentionally no
`MCPU_CPP_SYSTEM_<LANG>_INCLUDE_PATH` variables. Replacing
`MCPU_CPP_SYSTEM_INCLUDE_PATH` replaces the complete installed system-header
tree rather than adding another directory. An empty effective value disables
the configured system tree. Neither `-idirafter` nor
`MCPU_CPP_AFTER_INCLUDE_PATH` acquires automatic language subdirectories.
`#include_next` is intended for wrapper headers. It remembers the exact
physical entry of this effective search chain that supplied the current header
and resumes at the following entry. This allows an explicit wrapper to alter
policy and then continue into a sandbox or installed system tree without
copying the original header or hard-coding its absolute pathname. The
`"file"` and `<file>` forms are equivalent for `#include_next`, and logical
names established by `#line` do not affect physical search provenance.
`#pragma once` marks the current physical file as processed for the remainder
of the preprocessing run. Identity is the filesystem device/inode pair, not
the pathname spelling, so the same file reached through another relative name,
a symbolic link or a hard link is skipped. The exact active directive is
consumed by mcpu-cpp; an inactive `#pragma once` has no effect. Other pragmas
remain in output for later compiler stages.
Command-line forced files
-------------------------
`-imacros FILE` and `-include FILE` use one deterministic preprocessing
pipeline. Predefined macros are installed first, all command-line `-D`/`-U`
actions are then applied in their own command-line order, every `-imacros`
file is processed in command-line order, every `-include` file is processed in
command-line order, and only then does preprocessing enter the primary input.
The relative placement of `-imacros` and `-include` options in argv therefore
does not interleave the two classes: every `-imacros` always precedes every
`-include`.
An `-imacros` file goes through the ordinary preprocessing machinery, including
`#include`, macro definition/undefinition, conditional directives, `#lang`,
`#pragma once`, diagnostics and dependency tracking. Its normal preprocessing
output, including output from headers reached from that file, is discarded.
The resulting macro table and other preprocessing state remain available to
subsequent forced files and to the primary input. `-include` uses the same
preprocessing machinery but retains its normal output, as if the header had
been included immediately before the primary source.
The operand of either forced-file option has GNU-style command-line search
semantics. An absolute path is used directly. A relative path is searched
first in the current working directory and then through the ordinary quoted
include chain shown above. The physical directory of the primary input does
not receive the special first priority that a literal `#include "file"` inside
that source would receive. Once a forced file has been found, quoted includes
inside it are resolved normally relative to that file's physical directory,
and `#include_next` retains the search-chain provenance of the entry that found
it.
Forced files are ordinary physical dependencies. Files reached through CWD or
user include classes remain user dependencies; files found through `-isystem`,
the configured system tree, `-idirafter` or configured AFTER paths retain
system dependency class for `-MM`/`-MMD`.
Dependency generation
---------------------
`-M` preprocesses the translation unit and writes one make rule instead of
normal preprocessor output. The rule contains the main source file and every
physical header actually reached through the ordinary `#include` /
`#include_next` pipeline, including system headers. Repeated spellings,
symbolic links and hard links to the same physical file are recorded once.
Logical names established by `#line` are never dependency names.
`-MM` uses the same traversal but omits headers found through `-isystem`, the
configured MCPU system tree, `-idirafter` and configured AFTER paths, and also
omits headers reachable only as descendants of such a system header. Quote
versus angle include spelling does not decide whether a dependency is system.
If the same physical file is also reached directly through a user include
context, it remains a user dependency.
The default target is the input basename with its suffix replaced by the
configured object suffix (normally `.o`). In dependency-only `-M`/`-MM` mode,
`-MF FILE` selects the make-rule destination; without `-MF`, the existing
mcpu-cpp `-o FILE` destination remains available.
`-MD` and `-MMD` generate dependencies as a side effect and do **not** suppress
normal preprocessing output. `-MD` includes system headers like `-M`; `-MMD`
applies the same user-only filter as `-MM`. Without `-MF`, the dependency file
is `<input-basename>.d` in the current directory, or is derived from ordinary
`-o` output by replacing its suffix with `.d`. `-MF FILE` overrides that
automatic name, and `-MF -` sends the dependency rule to stdout.
Options `-MT` and `-MQ` are intentionally left for the next dependency stage.
#lang contract
--------------
The initial language state is `0`. It is the base MCPU language state and is
installed before preprocessing begins. `0` is deliberately not a parameter of
`#lang`.
`#lang` requires exactly one quoted language name:
#lang "diff"
The text inside the quotes is not escape-decoded. It must be a non-empty
single word without whitespace, and the closing quote must occur on the same
physical source line. After the closing quote only whitespace is allowed.
The accepted names come only from the internal language table: `diff`, `dift`,
`alg`, `as`, `avm`, and `ACS`. Matching is ASCII case-insensitive, so for
example `"diff"`, `"Diff"`, `"DIFF"` and `"dIfF"` all select the same
language. Unsupported names such as `"0"`, `"c"` and `"vasm"` are errors.
External whitespace is normalized when `#lang` is copied to output. For
example:
# lang "DiFf"
is emitted as:
#lang "DiFf"
The spelling inside the quotes is preserved as written.
`#lang` and `#endlang` form one translation-unit-wide stack. The stack is not
reset at an `#include` boundary. Therefore a `#lang` in one file and its
matching `#endlang` in another included file are intentionally legal, exactly
as required by the macro-expansion contract.
Both directives are passed through to output so the later language dispatcher
and parser layer can observe the same language boundaries. `#lang` is emitted
in the normalized form described above.
Macro operators
---------------
Function-macro stringification (`#`) is supported.
The operator uses the raw, unexpanded actual argument, removes leading and
trailing whitespace, folds internal whitespace outside quoted tokens to one
space, and escapes double quotes and backslashes as required by the resulting
quoted string. `##` token concatenation is also supported and rescans the concatenated token
through the ordinary macro-expansion path.
The completed predefined ABI/environment layer from 0.0.12 is preserved.
INT/UINT DECIMAL_DIG counts only decimal digits of the corresponding numeric
maximum, without a sign or terminating NUL. Integer decimal precision and
Real DECIMAL_DIG/MANT_DIG metadata cover every configured type width;
large MAX/MIN/EPSILON textual values remain capped at 256 bits.
`__MCPU_CPP_VERSION__` is the mcpu-cpp package version. `_ARCH_MCPU` identifies
the target. The LibMPU profile macros use the MCPU namespace:
`__MCPU_MACHINE_REGISTER_WIDTH__`, `__MCPU_REAL_IO_LIMIT__` and
`__MCPU_MATH_FN_LIMIT__`.
MCPU pointers are always 64 bits independently of the host. `intptr` and
`ptrdiff` are signed `int64`; `uintptr` is `uint64`:
__INTPTR_TYPE__ int64
__INTPTR_WIDTH__ 64
__INTPTR_MAX__ 0x7fffffffffffffff
__UINTPTR_TYPE__ uint64
__UINTPTR_WIDTH__ 64
__UINTPTR_MAX__ 0xffffffffffffffff
__PTRDIFF_TYPE__ int64
__PTRDIFF_WIDTH__ 64
__PTRDIFF_MAX__ 0x7fffffffffffffff
The configured LibMPU size and signed-size types are exposed only in the MCPU
namespace. For a 64-bit profile the public contract is:
__MCPU_SIZE_TYPE__ uint64
__MCPU_SIZE_WIDTH__ 64
__MCPU_SIZEOF_SIZE__ 8
__MCPU_SIZE_MAX__ 0xffffffffffffffff
__MCPU_SSIZE_TYPE__ int64
__MCPU_SSIZE_WIDTH__ 64
__MCPU_SIZEOF_SSIZE__ 8
__MCPU_SSIZE_MAX__ 0x7fffffffffffffff
Integer TYPE/WIDTH/SIZEOF metadata is generated for every power-of-two LibMPU
integer family through `NB_I_MAX * 8`. Real and Complex TYPE/WIDTH/SIZEOF
metadata is generated through the configured `MPU_REAL_IO_LIMIT`. Complex
WIDTH is the language type parameter: `complex128` has WIDTH 128 but SIZEOF 32
bytes, because it stores two real128 components.
Every available Real family also exports two compact conversion/layout
properties through `MPU_REAL_IO_LIMIT`: `__SIZEOF_REAL<bits>_EXP__` comes from
LibMPU `_sizeof_exp()`, while `__REAL<bits>_MAX_STRLEN__` comes from
`_real_max_string()`. MAX_STRLEN is a number of characters, not bytes; a
zero-terminated `char8` or `char16` buffer therefore needs at least
`MAX_STRLEN + 1` elements.
Large numeric textual values remain deliberately capped at 256 bits. Integer
MAX and Real MAX/MIN/EPSILON/exponent-value macros are not emitted above that
width. Integer DECIMAL_DIG and Real DECIMAL_DIG/MANT_DIG remain available for
the complete configured families. Integer MIN expressions are never
predefined. This keeps `-dMP` compact while preserving useful precision,
structural and text-buffer metadata for large LibMPU types.
The future language uses fixed-width names. Character types are `char8` and
`char16`; their sizes are `__SIZEOF_CHAR8__` and `__SIZEOF_CHAR16__`. Ordinary
C `char`, `short`, `int`, `long`, `wchar_t`, and the C-style `char*_t` names are
not part of the target language type model.
The effective `MCPU_CPP_SYSTEM_INCLUDE_PATH` automatically contributes a
language-specific subdirectory for each active switched language. For example,
with the runtime-derived root and `#lang "diff"`,
`<runtime-root>/include/diff` is searched before
`<runtime-root>/include`. These derived directories need not exist and
require no extra configuration variables.
`-dM` dumps only the current non-predefined macro table in deterministic
`#define` form. `-dMP` first dumps active predefined macros and then the
non-predefined macros; each group is sorted by name.
`-dconfig` dumps the effective configuration variables in sorted `NAME = value;`
form. `-v` prints the include-related effective configuration values once,
after all configuration layers have been resolved, and then preserves the usual
include/language runtime trace. `-dsearch-dirs` prints `search: DIR` lines for
the effective global include directories in semantic priority order and exits
without preprocessing. The dynamic source-directory step used by quoted
includes is not part of that global dump. None of these dump actions requires
an input file.
The dynamic source macros `__FILE__`, `__LINE__`, `__BASE_FILE__`,
`__INCLUDE_LEVEL__`, `__DATE__` and `__TIME__` use one translation-unit
source stack and timestamp. Stringification, concatenation and conditional
compilation are part of the current preprocessing contract.
Parser generation
-----------------
The #if expression parser is generated by ZUBR 4.1.0 from
`src/mcpp-expr.zubr`. The grammar also contains the UCS-2 lexical analyzer.
The generated source `src/mcpp-expr.c` is included in release archives, so a
normal build from a release archive does not require ZUBR.
The conditional-expression evaluator is deliberately 64-bit only. Integer
literals may use `U`/`u`, or the MCPU width suffix `zNNN[Uu]` / `ZNNN[Uu]`.
For valid widths up to 64, the low N bits are taken and then sign-extended
(`zNNN`) or zero-extended (`zNNNu`) to 64 bits; all later operations remain
64-bit and the original width is forgotten. Widths above 64 are rejected in
conditional directives. Invalid widths not exceeding 64 produce a warning
and the width suffix is ignored. C `L`/`LL` integer suffixes are not accepted.
`#error` and `#warning` are implemented as diagnostic directives. Their
arguments are not macro-expanded. Outside quoted tokens, whitespace sequences
are folded to one space for the diagnostic text. `#error` stops preprocessing;
`#warning` continues. Both directives disappear from normal and `-dD` output,
and both are ignored in inactive conditional branches.
Warning control follows a compact GNU-like model. `-Wcomment` and `-Wcomments`
enable warnings for `/*` inside an existing block comment and for a
backslash-newline continuing a `//` comment; `-Wall` currently enables this
optional warning class. The specific `-Wno-comment`/`-Wno-comments` setting
overrides `-Wall` regardless of command-line order. `-w` globally suppresses
all warnings, including `#warning`, invalid `zNNN` widths, macro redefinition,
invalid `##` paste and enabled comment diagnostics. It does not suppress
errors. `-Werror` promotes every warning that is actually emitted to an error
and unsuccessful preprocessing; because `-w` prevents warning emission first,
`-w -Werror` and `-Werror -w` are equivalent and successful when no independent
error occurs. Likewise, warning classes may be enabled by `-Wcomment` or
`-Wall`, but remain silent under `-w` regardless of option order. `-Wno-error`
restores ordinary warning severity. `-Werror` does not by itself enable
optional warning classes.
When `mcpp-expr.zubr` is changed, the ordinary Automake `.zubr.c` rule
regenerates the C source with:
zubr -vl -s -Bmcpp_ -o mcpp-expr.c mcpp-expr.zubr
Developer bootstrap
-------------------
A Git checkout may omit files that are regenerated mechanically, including
`configure`, `Makefile.in`, Automake helper scripts, `config.h.in`, `aclocal.m4`
and `src/mcpp-expr.c`. Regenerate them with:
```text
./bootstrap
```
`./bootstrap --target-dest-dir=DIR` follows the LibMPU/LibMPUIO convention for
using Autoconf macro/header directories from a target ROOTFS. Release archives
remain self-contained and do not require bootstrap before `configure`.
Build
-----
mcpu-cpp uses Autoconf/Automake and obtains LibMPUIO compilation and linker
flags from `mpuio-config`, following the conventions of LibMPU and LibMPUIO.
No pkg-config or flex dependency is introduced. The 1.0.3 release is
developed and tested against LibMPU 1.0.25 and LibMPUIO 1.0.4. The LibMPUIO
UCS-2 ctype API is required. ZUBR 4.1.0 is the parser-regeneration tool; it is
not required for a normal build from a release archive containing the generated
`src/mcpp-expr.c`.
A normal build is:
./configure --prefix=/usr --libdir=/usr/lib64
make
make tests
make install
|