Appendix A (Reference Manual) of the book broadened my outlook on programming languages by providing me a glimpse of what goes into formally specifying a programming language. Section A.12 (Preprocessing) of this appendix specifies trigraph sequences. Quoting from the section:
> Preprocessing itself takes place in several logically successive phases that may, in a particular implementation, be condensed.
> 1. First, trigraph sequences as described in Par.A.12.1 are replaced by their equivalents. Should the operating system environment require it, newline characters are introduced between the lines of the source file.
Then section A.12.1 (Trigraph Sequences) further elaborates trigraph sequences in more detail. Quoting this section below:
> The character set of C source programs is contained within seven-bit ASCII, but is a superset of the ISO 646-1983 Invariant Code Set. In order to enable programs to be represented in the reduced set, all occurrences of the following trigraph sequences are replaced by the corresponding single character. This replacement occurs before any other processing.
??= #
??/ \
??' ^
??( [
??) ]
??! |
??< {
??> }
??- ~
> No other such replacements occur.> Trigraph sequences are new with the ANSI standard.
As opposed to say, “Learn You a Haskell for Great Good! A Beginner's Guide” which is 881 pages and doesn’t even moderately cover the prelude.
Anyway, C is an amazing language and I keep a K&R on my phone as a pdf
Number of times I've seen trigraphs in "real code": still zero. I hope it's the same for you.
I don't remember whether trigraphs were not supported by the compiler at the time or whether we just wanted to avoid completely unreadable code. Not experienced in VM/370 administration we spent weeks to modify the system to use some international EBCDIC codepage.
The system never saw much use, everybody preferred Unix workstations where programming in C was a natural thing.
Great memories!
I've pasted it here for convenience (formatting fixed, thanks child comment!):
// Are you there god??/
??=define _(please, help)
??=define _____(i,m, v,e,r,y) r%:%:m
??=define ____ _____(a,f,r,a,i,d)
main(__)<%____(!_(-~-??-((-~-??-!__<<-
??-!!__)<<-??-(!!__<<!!__))+-~-~-??--~-~
-~-~-~-~-??-(-~-~-~-~-??-!!__<<-~!!__),-
??-!__))<%??>%>_(__,___)??<____
(printf("please let me die??/r%d bottle%s"
" of bee%s""""??/n",(!(___
%-~-~!!___))?--__+!___++:__+!___++,!(__-!!___)
&&___%-~-~!!___??!??!!(___%-~-~!!___??!??!__
-(-~!!___))?"":"s",___%-~-??-!!___<-??-!!___?
"r on the wall":"eeeeeeer! Take one down,pass ??/
it around")&&__&&_(__,___),"mercy I'm in pain")??<??>??>But trigraphs have gotten old even for IOCCC. In the guidelines for recent years, they specifically mention "We tend to dislike programs that ... obfuscate by excessive use of ANSI tri-graphs": https://www.ioccc.org/2020/guidelines.txt
For code blocks, prefix each line with two or more spaces.But think the cpp has to go away first, after enough sed.
https://grayson.sh/blogs/using-piphilology-to-hide-strings
https://www.gnu.org/software/gawk/manual/gawk.html#Signature...
while (x --\
\
\
\
> 0)
printf("%d ", x);> Almost every country needed an adapted version of ASCII, since ASCII suited the needs of only the US and a few other countries. For example, Canada had its own version that supported French characters.
> Many other countries developed variants of ASCII to include non-English letters (e.g. é, ñ, ß, Ł), currency symbols (e.g. £, ¥), etc. See also YUSCII (Yugoslavia).
> It would share most characters in common, but assign other locally useful characters to several code points reserved for "national use". […]
> Because the bracket and brace characters of ASCII were assigned to "national use" code points that were used for accented letters in other national variants of ISO/IEC 646, a German, French, or Swedish, etc. programmer using their national variant of ISO/IEC 646, rather than ASCII, had to write, and, thus, read, something such as
ä aÄiÜ = 'Ön'; ü
instead of { a[i] = '\n'; }
> C trigraphs were created to solve this problem for ANSI C, although their late introduction and inconsistent implementation in compilers limited their use. Many programmers kept their computers on US-ASCII, so plain-text in Swedish, German etc. (for example, in e-mail or Usenet) contained "{, }" and similar variants in the middle of words, something those programmers got used to. For example, a Swedish programmer mailing another programmer asking if they should go for lunch, could get "N{ jag har sm|rg}sar" as the answer, which should be "Nä jag har smörgåsar" meaning "No I've got sandwiches".http://www.righto.com/2019/11/ibm-sonic-delay-lines-and-hist...
The next-gen was far more common.. The IBM 3270 terminal hooked to a local controller that talked to the mainframe. Could also hook a printer to the controller, you could print screen and simple forms independently from the mainframe.
You know all this, but I've always thought it was cool, and try to refresh my understanding of the setup. I no doubt have many details wrong.
#include <iso646.h>
#include <stdbool.h>
#include <stdio.h>
#define is ==
bool is_whitespace(int c) {
if (c is ' ' or c is '\n' or c is '\t') {
return true;
}
return false;
}
int main() {
int current, previous;
bool in_word;
while ((current = getchar()) not_eq EOF) {
if (is_whitespace(current) and not is_whitespace(previous)) {
putchar('\n');
} else {
putchar(current);
}
previous = current;
}
return 0;
}I quite like them, but then again, I have been writing way too much python lately.
edited to add: I really like "Modern C" and just re-checked -- no mention of the preprocessor feature!
"Are question marks fine?"
"Yes."
"I'll come up with something."
"Whether it's computer languages or human ones, as soon as you get into a discussion about the correct parsing of a statement, you've lost and need to rewrite in a way that's unambiguous. Too many people pride themselves on knowing more or less obscure rules and, honestly, no one else cares."
I've used uppercase-only terminals, and I've used ancient C, but not at the same time.
Took me a while to figure out that "trigraph" was referring to some part of "??!?!!?!????" and not "WTF".
The latter is a very common idiom in Julia code, which I found obscure and puerile at first (“look how smart I am”), but have come to appreciate as concise and natural by now.
For example:
function fact(n::Int)
n >= 0 || error("n must be non-negative")
n == 0 && return 1
n * fact(n-1)
end
https://docs.julialang.org/en/v1/manual/control-flow/#Short-... #define and &&
#define and_eq &=
#define bitand &
#define bitor |
#define compl ~
#define not !
#define not_eq !=
#define or ||
#define or_eq |=
#define xor ^
#define xor_eq ^=
I suppose that allows for code like this: if (x or not y or not z) {
return 1;
}
https://en.wikipedia.org/wiki/C_alternative_tokens[1] https://stackoverflow.com/questions/1642028/what-is-the-oper...
https://en.wikipedia.org/wiki/BCPL
This is the earliest example of this sort of thing i'm aware of - is there an earlier example?
Also, BCPL supported // for comments, again, probably the first use of this sequence.
This comment on the SO post made my day. :D
1.c:1:11: warning: trigraph ??< ignored, use -trigraphs to enable [-Wtrigraphs]
Is there a preprocessor directive to enable support out of curiosity? int main() {
[](){}()
}
is still wierd.Wonder if there will be a request for an emacs macro to handle the replaced cpp trigraphs? [2]
[1] https://zygoloid.github.io/cppcontest2018.html [2] https://www.emacswiki.org/emacs/CppTemplate