F02, the preprocessor is not a text editor - #79
Merged
Merged
Conversation
The second lesson of M3. F01 showed the language the driver's decisions are written in. This one goes inside the first program the driver runs. The lesson is built on one demonstration: `#define EMPTY` and then `a+EMPTY+b`, which prints `a+ +b`. Text substitution gives `a++b`. The space is in no input file, and it is there because an empty expansion leaves a padding token and cpp_avoid_paste was asked whether `+` may be printed against `+`. Thirty eight token pairs were probed the same way. Twenty nine came back separated and nine did not, and the nine are the ones that do not lex as a single token. gxray.cpp reads recorded preprocessor output: macro tables, line markers, -H traces, expansion demonstrations. It never expands a macro, because expanding one needs the include path, the target's macros, the date and a hash table with the state of every other macro in it. The corpus holds five macro tables, six expansions, the thirty eight probes and four include traces, on aarch64 Darwin and x86-64 Linux. 443 macros against 408, 373 names shared, and 50 of the shared names defined to different text, which is the set #ifdef cannot see. One #include <stdio.h> opens 42 files on one machine and 31 on the other, and the two sets have no path in common. Four headers differing by one line measure the multiple include optimization: a comment after the #endif costs nothing, one declaration costs the whole thing. -H names neither of them, because report_missing_guard only reports a file whose stack_count is 1, so the advice arrives for the header that has not cost anything yet. tests/test_cpp.py compares the table of paste-avoiding pairs against the case labels of cpp_avoid_paste in the pinned tree, so a GCC that grows a case fails the build rather than making a paragraph quietly false. BP-CPP goes from stub to partial: the 32-byte token and its 13 flags, the 90 token types and the four positions in the table, the hash node, the macro, the 20 built-ins of which -dM prints one; cpp_get_token_1, argument prescan, pasting by re-lexing, the two flags behind blue paint, the nine places that maintain the include guard state, and the printer's two independent reasons to print a space. The glossary's `token` entry said twenty four bytes. libcpp's own comment says thirty two on a 64-bit host.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The second lesson of M3, and the first blueprint of the front end.
The lesson
One demonstration carries it.
#define EMPTY, thena+EMPTY+b, andgcc -E -Pprintsa+ +b. Text substitution givesa++b. The space is in no input file. It is there because the empty expansion left a padding token andcpp_avoid_pastewas asked whether+may be printed against+.Thirty eight token pairs were probed by putting them next to each other with nothing between them. Twenty nine came back separated, nine did not, and the nine are exactly the ones that do not lex as a single token.
The rest of the lesson is the same point from four other directions:
#ifdefcannot see.-std=c23changes one name and no values, because the default was alreadygnu23.-dM, which claims to print every macro.__FILE__and__LINE__are among the missing. The one that shows up is__STDC__, becausecpp_init_builtinsdefines it a second time the ordinary way.#endifcosts nothing. One declaration costs the multiple include optimization.-Hnames neither, becausereport_missing_guardonly reports a file whosestack_countis 1, so the advice arrives for the header that has not cost anything yet.#include <stdio.h>opens 42 files on aarch64 Darwin and 31 on x86-64 Linux, and the two sets have no path in common.What is new
gxray/cpp.py, a reader for recorded preprocessor output. It never expands a macro: expanding one needs the include path, the target's macros, the date and a hash table with the state of every other macro in it, and a lesson that faked those would teach a fiction.corpora/cpp/f02.json, five macro tables, six expansions, thirty eight probes and four include traces, on two targets.tests/test_cpp.py, including a check that compares the table of paste-avoiding pairs against the case labels ofcpp_avoid_pastein the pinned tree. A GCC that grows a case fails the build rather than making a paragraph quietly false.blueprints/BP-CPP.md, stub to partial.BP-CPP
The 32-byte token and its 13 flags, the 90 token types and the four names in the table that are positions rather than types, the hash node, the macro, the 20 built-ins. Then
cpp_get_token_1, argument prescan and why the two step stringify idiom exists, pasting as spelling-then-re-lexing and why##on two macros that expand to+givesPLUSPLUSwith no diagnostic, the two flags behind blue paint, the nine places that maintain the include guard state without ever scanning a file, and the printer's two independent reasons to print a space.Section 9 has the boundary the whole lesson is about: two compilers that share no header file and disagree about 50 macro values agree exactly on what
#define foo foo + 1does.Also
The glossary's
tokenentry said twenty four bytes. libcpp's own comment atlibcpp/include/cpplib.h:261says thirty two on a 64-bit host.Gates
pytest1560 passed,nbbuild run19 lessons,refcheck,prosecheck,bpc check,bpc pages --check,tier0 check/offline/online,dumpparse,ruff, glossary, all green.matrix digests --checkstill reports the two missingbootdigests, which is the M2 gap tracked in #6 and unrelated to this branch.