GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — token stream expander by danmar · Pull Request #758 · cppcheck-opensource/simplecpp Conversation
|
| Name | Meaning |
|---|---|
Input::pending |
Tokens that are scanned again. The next token to read is at the front. |
Input::raw |
The next token of the source. It is only read when a macro call needs its arguments. |
| End-of-expansion marker | A special token ("\x01", it cannot occur in source) that is placed after the result of a macro. Token::macro is the name of the macro that ends. |
disabled |
std::map<name, int>: the number of active expansions of each macro. A macro is disabled when the count is not 0. |
Token::painted |
The name has been found while its macro was disabled. It is never expanded, also not later. |
expand(output, rawtok) starts with the macro name as the onlandInput`:
while there are pending tokens:
tok = read() # a name that is read whpainted
if tok is not a macro, or is painted:
output tok; continue
if the macro is function-like and the next token is not '(':
output tok; continue # not a call
if the macro is built-in (__FILE__, __LINE__, __COUNTER__):
output its value; continue
read the call (tokens from pending first, then the source)
if the call is not terminated:
put the tokens after the name back; output tok; continue
result = substitute(macro, call)
push result in front of pending
push a marker for the macro after the result, and disable
Reading a marker enables its macro again (count - 1). Markers in front of the next token are read by hasPending(), peek() and read(), so the macro is
enabled again at exactly the point where its replacement has b
Input with only that argument's tokens).Both versions are kept.# → stringified from the argument as written## → the argument as written## pastes the last token of the result with the first tokperand is a placemarker. The checks for invalid pasting (invalid token, universal character name, etc.) are the ones that Macro already had.__VA_OPT__(...) is replaced by its content, or by nothingexpand to nothing (C23 6.10.5.1). #__VA_OPT__ and ##__VA_OPT__ are handled as # and ## of that result.defined X and defined(X) in a replacement list are repl X is a parameter or A ## B.Token::macro set and the location of the macro call, so the usual output (-l, Token::macro) is the same as before.The macro is disabled from the moment its result is pushed back until the marker after it has been read. Anything that is read in between is part of the
rescan of that expansion, so:
#define f f, A → B → A)while the macro is disabled, so it is painted and stays.f(f)(1) with #define f(x) x): the argument f is not disabled while arguments are expanded, but it isf is disabled. It is pa is not a call. The result is f ( 1 ), as with gcc.#define g f / g(1)): f is read when g is already enabled again(), so f(1) is expanded. This No special code is needed; it is just the next token in thestream.DEFER / EXPAND / R_I idiom more scan of the tokens, so one more level is expanded. Apainted name stays painted, so it is never expanded too early.A function-like macro name followed by ( whose ) is never found is not a call. startLog() records the markers that are read while the arguments are
collected, and pushBackLogged() puts the tokens back with s (and disables the macros again). The name is output as itis. This is what makes R at the end of the file work.
Prosser's algorithm gives each token a hide set and computes intersections for function-like macros. This expander gets the same results with a counter
per macro and one marker per expansion, which is how e.g. GCC'g's DisableMacro work. There is nothing to copy per token, and Token does not need to know about Macro.
Each argument is expanded once (not once per use, as before), and each token is pushed back once per macro that produces it. Macro usage is reported once
per call.
Two statements in it come from my reading of the code, not fro
I can cut them.
Each reproducer was run with master and with this branch, and compared with gcc -E.
| Issue | Input | master | this PR (same as gcc) |
|---|---|---|---|
| #742 | #define f(x) x / f(f)(1) |
1 |
f ( 1 ) |
| #742 | #define R() / R at end of file |
syntax error | R |
| #226 | EVAL ( HELLO EMPTY() () ) |
HELLO ( ) |
"Hello World" |
| #259 | ADVANCED(int, , foo, ) (## with empty args) |
Invalid ## usage |
int foo ; |
| #378 | a ## b ## c, a ## ## c |
x# "y" |
xy |
| #394 | MACRO_COMPOSE(FOO, BAR(1)) |
FOOBAR ( 1 ) |
1 |
| #458 | # and ## with __VA_OPT__ |
#, "x", "Hello" world |
"\"x\"", "Helloworld" |
| #469 | N4917 __VA_OPT__(G R X) example |
Wrong number of parameters |
int x = 42 ; |
| #491 | HASH define foo bar |
# and define… on separate lines |
# define foo bar |
| #656 | PAR-style PREFIX_ ## kind indirection |
PREFIX_SCALAR ( int , x ) |
int x |
Closes #226, #259, #378, #394, #458, #469, #491, #656.
Implements the goal of #742 with a different algorithm, see above.
macro_paste_hard.c and macro_rescan_varargs.c now match gcc/clang and are removed from the todo list in run-tests.py.pr58844-1.c and pr58844-2.c are added to skip. For x######x, gcc gives xx and clang gives x##x. Master happened to give x##x, this PR gives xx. On macOS gcc is clang, so only the clang result is accepted there.defined(a ## b) inside a macro used__attribute()##8; still pastes )8.simplecpp::Macro::expandToken() #662, fuzzing crash in Macro::expandToken() #414: the fuzzing inputs do not crash on master, with ken` code they pointed to is removed, but I cannot claim a fix.# that comes from a macro at the start of a line gets a space before it, so the output is not read as a directive (as gcc and clang do). See # in macro left in original location #491.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Rescanning and recursion, __VA_OPT__, and a # from a macro at the start of a line. The results are the same as gcc gives. Where the current macro expansion gives another result, it is a TODO_ASSERT_EQUALS. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
7fbc39d to
87a51e7
Compare
The result of an expansion is pushed back onto the input followed by an end-of-expansion marker, and is scanned again. A macro is disabled from the moment its result is pushed until the marker has been read, also while the arguments of a call are collected. A name that is read while its macro is disabled is painted and never expanded. It is not used yet. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Fixes the R/DEFER/EXPAND recursion idiom, __VA_OPT__ after expansion, # and ## with __VA_OPT__, pasting that is rescanned, and a function-like macro name at the end of the file (it is not a call). A # that comes from a macro at the start of a line gets a space before it, as gcc and clang do, so the output is not read as a directive. The tokens of an expansion now have the location of the macro call. The tests that had a TODO for the old expansion are updated. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Macro::expand and the functions only it used, and Token::mExpandedFrom. Macro keeps parsing of the definition, the argument handling and the checks that TokenStreamExpander uses. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
87a51e7 to
e9fd2ad
Compare
|
This was written 100% by AI. Let's treat this code as a proof-of-concept. If we like the idea we can start thinking about how it can be merged safely in small steps.. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
EVAL(...) not working
There was an error while loading. Please reload this page.