[HN Gopher] The earliest versions of the first C compiler known ...
       ___________________________________________________________________
        
       The earliest versions of the first C compiler known to exist
        
       Author : diginova
       Score  : 279 points
       Date   : 2025-03-21 08:17 UTC (3 days ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | diginova wrote:
       | Also read how a compiler can be written in the same language -
       | https://en.wikipedia.org/wiki/Bootstrapping_%28compilers%29
       | 
       | https://stackoverflow.com/a/18247926/15566831
        
       | Joker_vD wrote:
       | Funnily enough, it is emphatically _not_ a single-pass compiler.
        
         | dbrower wrote:
         | I don't think anybody thinks or thought it was.
        
       | aap_ wrote:
       | Probably one of my favorite pieces of software of all times.
       | Learned so much from this!
        
         | gus_massa wrote:
         | Do you remember any interesting anecdote you can share?
        
           | aap_ wrote:
           | Anecdote probably not. But i learned how a compiler works
           | from it and reconstructed the B compiler based on it (found
           | here: https://github.com/aap/b, warning: repo is messy, will
           | clean up more soon hopefully).
        
       | bluetomcat wrote:
       | Interesting usage of "extern" and "auto". Quite different from
       | contemporary C:                   tree() {             extern
       | symbol, block, csym[], ctyp, isn,             peeksym, opdope[],
       | build, error, cp[], cmst[],             space, ospace, cval,
       | ossiz, exit, errflush, cmsiz;                  auto op[],
       | opst[20], pp[], prst[20], andflg, o, p, ps, os;             ...
       | 
       | Looks like "extern" is used to bring global symbols into function
       | scope. Everything looks to be "int" by default. Some array
       | declarations are specifying a size, others are not. Are the
       | "sizeless" arrays meant to be used as pointers only?
        
         | Joker_vD wrote:
         | Yes, pretty much. To be fair, C at this point was basically
         | BCPL with slightly different syntax (and better char/string
         | support). The introduction of structs (and then longs) changed
         | it forever.
        
         | netbsdusers wrote:
         | You can do the same with a modren C compiler - the extern and
         | auto mean the same and int is still the default type.
        
           | tialaramex wrote:
           | In C23, auto doesn't have a default type, if you write auto
           | without a type then you get the C++ style "type deduction"
           | instead. This is part of the trend (regretted by some WG14
           | members) of WG14 increasingly serving as a way to fix the
           | core of C++ by instead mutating the C language it's
           | ostensibly based on.
           | 
           | You can think of deduction as crap type inference.
        
             | pjmlp wrote:
             | Design by committee, the outcome is usually not what the
             | people on the trenches would like to get.
        
               | uecker wrote:
               | Nobody in the trenches seemed to use old-style auto in
               | the last decades.
               | 
               | BTW: The right place to complain if you disagree would be
               | the compiler vendors. In particular the Clang side pushes
               | very much for keeping C and C++ aligned, because they
               | have a shared C/C++ FE. So if you want something else,
               | please file or comment on bugs in their bug tracker.
               | Similar for other compilers.
        
               | Y_Y wrote:
               | > Nobody in the trenches seemed to use old-style auto in
               | the last decades.
               | 
               | To the beat of my knowledge, there was no case where
               | "auto" wasn't redundant. See e.g.
               | https://stackoverflow.com/a/2192761
               | 
               | This makes me feel better about repurposing it, but I
               | still hate the shitty use it's been put to.
        
               | pjmlp wrote:
               | Indeed, however many in the treches would like a more
               | serious take on security, complaining has not served
               | anything in the last 50 years until goverment agencies
               | finally decided to step in.
        
               | uecker wrote:
               | This is again a problem compilers could have addressed,
               | but didn't. Mostly because the users in the trenches did
               | not care. Instead they flocked in droves to the compiler
               | optimizing in the most aggressive way and rejecting
               | everything costing performance. So I do not have the
               | feeling that users were really pushing for safety. They
               | are very good at complaining though.
        
               | ndiddy wrote:
               | GCC and Clang support asan/ubsan, which lets you trade
               | performance for nicer behavior related to memory access
               | and undefined behavior. Whenever I do C development for a
               | platform that supports asan/ubsan, I always develop and
               | test with them enabled just because of how much debugging
               | time they save.
        
               | jeltz wrote:
               | Isn't the original inclusion of the auto keyword more in
               | line with what you expect from design by committee?
               | Including a keyword which serves no purpose other than
               | theretical completeness?
        
               | pjmlp wrote:
               | I was talking more in general, not specific regarding
               | auto.
               | 
               | Actually I did use C compilers, with K&R C subset for
               | home computers, where auto mattered.
               | 
               | Naturally they are long gone, this was in the early
               | 1990's.
        
               | windward wrote:
               | It doesn't matter. The people in the trenches don't
               | update their standard versions.
        
               | mhh__ wrote:
               | An interesting secondary meaning of "design by
               | committee", the reason why what you mention happens, is
               | "design _in_ committee ".
               | 
               | People can skip the usual lifecycle and feedback for an
               | idea by presenting jumping directly to committee stage./
        
         | fsckboy wrote:
         | > _Looks like "extern" is used to bring global symbols into
         | function scope._
         | 
         | a better way to think of extern is, "this symbol is not
         | declared/defined/allocated here, it is
         | declared/defined/allocated someplace else"
         | 
         | "this is its type so your code can reference it properly, and
         | the linker will match up your references with the
         | declared/defined/allocated storage later"
         | 
         | (i'm using reference in the generic english sense, not pointer
         | or anything. it's "that which can give you not only an r-value
         | but an l-value")
        
         | dfawcus wrote:
         | As to "sizeless" arrays - yes.
         | 
         | Have a look at the early history of C document on DMR's site,
         | it mentions that the initial syntax for pointers was that form.
        
         | xenadu02 wrote:
         | "auto" used to mean _automatic memory management_ because if
         | you are coming from assembly or even some other older higher-
         | level languages you can 't just declare a local variable and
         | use it as you please. You must declare somewhere to store it
         | and manage its lifetime (even if that means everything is
         | global).
         | 
         | C and its contemporaries introduced automatic or in modern
         | terms local or stack allocated values, often with lexically-
         | scoped lifetimes. extern meaning something outside this file
         | declares the storage for it and register meaning the compiler
         | should keep the value in a register.
         | 
         | However auto has always been the default and thus redundant and
         | style-wise almost no one ever had the style of explicitly
         | specifying auto so it was little-used in the wild. So the C23
         | committee adopted auto to mean the same as C++: automatically
         | infer the type of the declaration.
         | 
         | You can see some of B's legacy in the design of C. Making
         | everything int by default harkens back to B's lack of types
         | because everything was a machine word you could interpret
         | however you wanted.
         | 
         | Also with original C's function declarations which don't really
         | make sense. The prototype only declares the name and the local
         | function definition then defines (between the closing paren and
         | the opening brace) the list of parameters and their types.
         | There was no attempt whatsoever to have the compiler verify you
         | passed the correct number or types of parameters.
        
       | tanelpoder wrote:
       | The first publicly available version of Oracle Database (v2
       | released in 1979) was written in assembly for PDP-11. Then Oracle
       | rewrote v3 in C (1983) for portability across platforms. The
       | mainframes at the time didn't have C compilers, so instead of
       | writing a mainframe-specific database product in a different
       | language (COBOL?), they just wrote a C compiler for mainframes
       | too.
        
         | chasil wrote:
         | UNIX was ported to the System/370 in 1980, but it ran on top of
         | TSS, which I understand was an obscure product.
         | 
         | "Most of the design for implementing the UNIX system for
         | System/370 was done in 1979, and coding was completed in 1980.
         | The first production system, an IBM 3033AP, was installed at
         | the Bell Laboratories facility at Indian Hill in early 1981."
         | 
         | https://web.archive.org/web/20240930232326/https://www.bell-...
        
         | dboreham wrote:
         | Writing something that large in assembly is pretty crazy, even
         | in 1979!
        
           | acchow wrote:
           | Keep in mind, Oracle was designed to run with 128KB of RAM
           | (no swapping). So it was really tens of thousands of lines,
           | not millions.
        
       | dark-star wrote:
       | My favorite function, which some might say even made it into
       | Windows ;-)                   waste()  /* waste space */
       | {          waste(waste(waste),waste(waste),waste(waste));
       | waste(waste(waste),waste(waste),waste(waste));
       | waste(waste(waste),waste(waste),waste(waste));
       | waste(waste(waste),waste(waste),waste(waste));
       | waste(waste(waste),waste(waste),waste(waste));
       | waste(waste(waste),waste(waste),waste(waste));
       | waste(waste(waste),waste(waste),waste(waste));
       | waste(waste(waste),waste(waste),waste(waste));         }
        
         | retSava wrote:
         | But why? Waste (compiled) binary space? Or source code space,
         | perhaps for early employee metrics gaming purposes?
         | 
         | And don't answer "to waste space of course" please. :)
        
           | ajb wrote:
           | There's nothing in that repo that says, but at a guess: old
           | machines often had non-uniform ways to access memory, so it
           | may have been to test that the compiler would still work if
           | the binary grew over some threshold.
           | 
           | Even today's machines often have a limit as to the offset
           | that can be included in an instruction, so a compiler will
           | have to use different machine instructions if a branch or
           | load/store needs a larger offset. That would be another thing
           | that this function might be useful to test. Actually that
           | seems more likely.
           | 
           | It might be instructive to compare the binary size of this
           | function to the offset length allowed in various PDP-11
           | machine instructions
        
             | bustling-noose wrote:
             | Yes it seems like this is something to do with hardware
             | testing. Maybe memory or registers or something that needed
             | just X bytes etc for overflows or something. It's really
             | random and the only person who would know it is the one who
             | wrote it :)
        
           | bluetomcat wrote:
           | Wild guess: it was a way to offset the location of the "main"
           | function by an arbitrary amount of bytes. In the a.out binary
           | format, this translates to an entry point which is not zero.
        
           | aap_ wrote:
           | See my other comment.
        
           | jeltz wrote:
           | One possible reason is to allocate a static global area.
           | Without read-only protection of memory you could write to
           | that area.
        
           | unclad5968 wrote:
           | There is a variable declared right before the waste space
           | function. The 'wasted' space is statically allocated memory
           | for the variable 'ospace' just before it.
        
           | dfawcus wrote:
           | http://cm.bell-labs.co/who/dmr/primevalC.html
           | 
           | " A second, less noticeable, but astonishing peculiarity is
           | the space allocation: temporary storage is allocated that
           | deliberately overwrites the beginning of the program,
           | smashing its initialization code to save space. The two
           | compilers differ in the details in how they cope with this.
           | In the earlier one, the start is found by naming a function;
           | in the later, the start is simply taken to be 0. This
           | indicates that the first compiler was written before we had a
           | machine with memory mapping, so the origin of the program was
           | not at location 0, whereas by the time of the second, we had
           | a PDP-11 that did provide mapping. (See the Unix History
           | paper). In one of the files (prestruct-c/c10.c) the kludgery
           | is especially evident. "
        
         | ape4 wrote:
         | The comment is a waste too. It could have explained why the
         | function is doing what it does.
        
       | ChrisMarshallNY wrote:
       | This is my favorite function :):
       | https://github.com/mortdeus/legacy-cc/blob/936e12cfc756773cb...
        
         | johnisgood wrote:
         | What is the point of it?
        
           | rasjani wrote:
           | Without actually knowing, i'd guess that would generate
           | bytecode's that could be modified later by patching the
           | resulting binary ?
           | 
           | I remember few buddies using similar pattern in ASM that just
           | added n NOP's into code to allow patching and thus
           | eliminating possible recompilation..
        
             | ChrisMarshallNY wrote:
             | I suspect that's it.
             | 
             | There was a lot of self-modification, going on, in those
             | days. Old machine language stuff had _very_ limited
             | resources, so we often modified code, or reused code space.
        
           | agumonkey wrote:
           | warm up the stack ? (no idea to be honest)
        
           | tanelpoder wrote:
           | The C alternative for the hardware "halt and catch fire"
           | instruction?
        
             | kps wrote:
             | The C alternative for the Fortran COMMON block.
        
           | aap_ wrote:
           | It's an awkward way to reserve memory. The important detail
           | here is that both compiler phases do this, and the way the
           | programs are linked guarantees that the reserved region has
           | the same address in both phases. Therefore an expression tree
           | involving pointers can be passed to the second phase very
           | succinctly. Not pretty, no, but hardware limitations force
           | you to do come up with strange solutions sometimes.
        
             | johnisgood wrote:
             | Thank you! Is it relevant today at all, or is there an use-
             | case for it today?
        
               | aap_ wrote:
               | No, if you need fixed addresses i suppose a linker script
               | would be the way to go? Or in this case you'd just
               | serialize the data such that it doesn't contain any
               | pointers in the first place.
        
               | ddulaney wrote:
               | If you even can with ASLR. Most modern boxes would
               | disable stuff like this.
        
               | mananaysiempre wrote:
               | There are better tools to do this these days--with the
               | GNU toolchain, for example, you'd use a linker script and
               | make sure you're building a non-position-independent
               | static executable. Alternatively, you could use self-
               | relative pointers: instead of having foo_t *foo and
               | putting p there, have ptrdiff_t foo and put ((char *)p -
               | (char *)&foo) there.
        
             | colejohnson66 wrote:
             | Here's the actual code that references the 'ospace' from
             | before 'waste': https://github.com/mortdeus/legacy-
             | cc/blob/936e12cfc756773cb...
        
           | fxtentacle wrote:
           | It's an obscure way to statically allocate memory for the
           | ospace pointer.
        
         | Amlal wrote:
         | Ah, yes, was that because of a lack of inline assembly? I feel
         | like these could be replaced by 'nop' operations.
        
         | arp242 wrote:
         | Gotta love the user-friendliness of these old Unix tools:
         | if (argc<4) {           error("Arg count");           exit(1);
         | }
        
       | FeistySkink wrote:
       | Missed opportunity not calling it LegaC.
        
       | smackay wrote:
       | 1972 is the answer to the question on the lips of everybody too
       | busy to look at the source files.
        
       | ModernMech wrote:
       | I thought the first C compiler was written in B.
        
         | indoordin0saur wrote:
         | Yes. I'm not an expert in compilers, but how is the first c
         | compiler also written in C? How did they compile the compiler?
        
           | ModernMech wrote:
           | There's a thread here which talked about it:
           | https://news.ycombinator.com/item?id=26721305
        
         | 9rx wrote:
         | If we had the full change history you would see that it is
         | written in B. New features were added and changes were
         | iteratively made along the way, but it is the same codebase.
         | Nowadays we'd pick some change point and call it B v2, but back
         | then they named that point C.
        
           | xenadu02 wrote:
           | That's not quite correct. See my comment here:
           | https://news.ycombinator.com/item?id=43465698
           | 
           | B was bootstrapped in BCPL, then rewritten in B to be self-
           | hosting. But the transition from B to NB (New B) to C was
           | continuous evolution. Thompson or Richie would add a feature
           | to the compiler, compile a new compiler, then change the
           | compiler source to use the new feature. If you did not have a
           | sufficiently new enough B/NB/C compiler you could not compile
           | the compiler and there was no path maintained to deal with
           | that. You went down the hall and asked someone else to give
           | you the newer compiler.
           | 
           | There also wasn't a definitive point where NB became C...
           | they just decided it had changed enough and called it C.
        
       | 90s_dev wrote:
       | The thing I always loved about C was its simplicity, but in
       | practice it's actually very complex with tons of nuance. Are
       | there any low level languages like C that actually _are_ simple,
       | through and through? I looked into Zig and it seems to approach
       | that simplicity, but I have reservations that I can 't quite put
       | my finger on...
        
         | bewo001 wrote:
         | forth would come to mind, some people have build surprising
         | stuff with it though I find it too low-level.
        
         | ChrisRR wrote:
         | It depends what you mean by simple. C still is simple, but it
         | doesn't include a lot of features that other languages do, and
         | to implement them in C is not simple.
         | 
         | C is simple for some use cases, and not for others.
        
           | 90s_dev wrote:
           | The appeal of C is that you're just operating on raw memory,
           | with some slight conveniences like structs and arrays. That's
           | the beauty of its simplicity. That's why casting a struct to
           | its first argument works, why everything has an address, or
           | why pointer arithmetic is so natural. Higher level langs like
           | C++ and Go try to retain the usefulness of these features
           | while abstracting away the actuality of them, which is
           | simultaneously sad and helpful.
        
             | colejohnson66 wrote:
             | > That's why casting a struct to its first argument works
             | 
             | Until WG14 makes everything you love about C "undefined
             | behavior" in the name of performance.
        
               | worik wrote:
               | > Until WG14 makes everything you love about C "undefined
               | behavior" in the name of performance.
               | 
               | What do you mean?
               | 
               | I just looked up WG14 and I cannot see what you mean
               | 
               | A link perhaps? Am I going to have to "pin" my C compiler
               | version?
        
               | tialaramex wrote:
               | Some people have this idea that when they write utter
               | nonsense it _should_ do what they meant because - ie they
               | 're missing out the whole discipline of programming and
               | going straight from "I want it to work" to "It should
               | work" and don't understand what they're doing wrong.
               | 
               | For some of these people WG14 (the C language sub-
               | committee of SC22, the programming language sub-committee
               | of JTC1, the Joint Technical Commitee between ISO and the
               | IEC) is the problem because somehow they've taken this
               | wonderful language where you just write stuff and it
               | definitely works and does what you meant and turned into
               | something awful.
               | 
               | This doesn't make a whole lot of sense, but hey, they
               | wrote nonsense and they're angry that it didn't work, do
               | we expect high quality arguments from people who mumble
               | nonsense and make wild gestures on the street because
               | they've imagined they are wizards? We do not.
               | 
               | There are others who blame the compiler vendors, this at
               | least makes a little more sense, the people who write
               | Clang are literally responsible for how your nonsense C
               | is translated into machine code which does... something.
               | They probably couldn't have read your mind and ensured
               | the machine code did what you wanted, especially because
               | your nonsense doesn't mean that, but you can make an
               | argument that they might do a better job of communicating
               | the problem (C is pretty hostile to this, and C
               | programmers no less so)
               | 
               | For a long time I thought the best idea was to give these
               | people what they ostensibly "want" a language where it
               | does something very specific, as a result it's slow and
               | clunky and maybe after you've spent so much effort to
               | produce a bigger, slower version of the software a friend
               | wrote in Python so easily these C programmers will snap
               | out of it.
               | 
               | But then I read some essays by C programmers who had
               | genuinely set out on this path and realised to their
               | horror that their fellow C programmers don't actually
               | agree what their C programs mean, the ambiguity isn't
               | some conspiracy by WG14 or the compiler vendors, it's
               | their reality, they are bad at writing software. The
               | whole point of software is that we need to explain
               | exactly what the machine is supposed to do, when we write
               | ambiguous programs we are doing a bad job of that.
        
             | windward wrote:
             | A typical C program of useful length typically includes a
             | spattering of implicit type conversions that the programmer
             | never intended or considered. It's the consequence of a
             | feature that abstracts away how the type system and memory
             | really[1] acts.
             | 
             | [1]for certain definitions of 'really'
        
           | acuozzo wrote:
           | > C still is simple
           | 
           | Syntactically, yes. Semantically, no.
           | 
           | There are languages with tons of "features" with far, far
           | less semantic overhead than C.
           | 
           | https://blog.regehr.org/archives/767
           | 
           | FWIW, writing programs in C has been my day job for a long
           | time.
        
             | drob518 wrote:
             | Exactly. There is a lot happening implicitly in a C program
             | that the programmer has to be aware of and keep in mind.
             | And it's made worse by valid compile implementation
             | choices. I remember chasing a bug for a day that was based
             | on me forgetting that the particular implementation I was
             | working with had signed characters and was sign extending
             | something at an inopportune time.
        
             | kstrauser wrote:
             | C is simple like SMTP.
        
               | acuozzo wrote:
               | EXACTLY!
        
             | groos wrote:
             | As someone who has had to parse C syntax for a living, I'd
             | argue that it's not syntactically simple either.
             | (Declarators are particularly nasty in C and even more so
             | in C++).
        
           | SAI_Peregrinus wrote:
           | I've said before that C is small, but not simple.
           | 
           | Turing Tarpits like Brainfuck or the Binary Lambda Calculus
           | are a more extreme demonstration of the distinction, they can
           | be very tiny languages but are extremely difficult to
           | actually use for anything non-trivial.
           | 
           | I think difficulty follows a "bathtub" curve when plotted
           | against language size. The smallest languages are really hard
           | to use, as more features get added to a language it gets
           | easier to use, up to a point where it becomes difficult to
           | keep track of all the things the language does and it starts
           | getting more difficult again.
        
         | LPisGood wrote:
         | It's not really clear to me how you could have a simple low
         | level language without tons of nuance. Something like Go is
         | certainly simple without tons of nuance, but it's not low
         | level, and I think extending it to be low level might add a lot
         | of nuance.
        
         | bluetomcat wrote:
         | The perceived complexity from a semantic standpoint comes from
         | the weakly-typed nature of the language. When the operands of
         | an expression have different types, implicit promotions and
         | conversions take place. This can be avoided by using the
         | appropriate types in the first place. Modern compilers have
         | warning flags that can spot such dodgy conversions.
         | 
         | The rest of the complexity stems from the language being a thin
         | layer over a von Neumann abstract machine. You can mess up your
         | memory freely, and the language doesn't guarantee anything.
        
           | drob518 wrote:
           | Exactly. C started off as high level assembly language. That
           | was both a strength, at first, and a weakness, later on.
        
         | grandempire wrote:
         | C is simple.
         | 
         | Representing computation as words of a fixed bit length, in
         | random access memory, is not (See The Art of Computer
         | Programming). And the extent to which other languages simplify
         | is creating simpler memory models.
        
           | mort96 wrote:
           | What about C is simple? Its syntax is certainly not simple,
           | it's hard to grok and hard to implement parsers for, and
           | parsing depends on semantic analysis. Its macro system is
           | certainly not simple; implementing a C preprocessor is a huge
           | job in itself, it's much more complex than what appears to be
           | necessary for a macro system or even general text processor.
           | Its semantics are not simple, with complex aliasing rules
           | which just exist as a hacky trade-off between programming
           | flexibility and optimizer implementer freedom.
           | 
           | C forces programs to be simple, because C doesn't offer ways
           | to build powerful abstractions. And as an occasional C
           | programmer, I enjoy that about it. But I don't think it's
           | simple, certainly not from an implementer's perspective.
        
             | pjc50 wrote:
             | Parsing isn't too bad compared to, say, Perl.
             | 
             | The preprocessor is a classic example of simplicity in the
             | wrong direction: it's simple to _implement_ , and pretty
             | simple to describe, but when actually using it you have to
             | deal with complexity like argument multiple evaluations.
             | 
             | The semantics are a disaster ("undefined behavior").
        
               | mort96 wrote:
               | I would probably describe Perl as really complex to parse
               | as well if I knew enough about it. Both are difficult to
               | parse compared to languages with more "modern
               | sensibilities" like Go and Rust, with their nice mostly
               | context free grammars which can be parsed without
               | terrible lexer hacks and separately from semantic
               | analysis.
               | 
               | Walter Bright (who, among other things, has been employed
               | to work on a C preprocessor) seems to disagree that the C
               | preprocessor is simple to implement:
               | https://news.ycombinator.com/item?id=20890749
               | 
               | > The preprocessor is fiendishly tricky to write. [...] I
               | had to scrap mine and reimplement it 3 times.
               | 
               | I have seen other people in the general "C
               | implementer/standards community" complain about it as
               | well.
        
               | pjc50 wrote:
               | I wonder if we can dig out the original K&R preprocessor
               | implementation?
        
               | dbrower wrote:
               | it was a lot simpler in capabilities. much of the
               | complexity is because of feature creep.
        
               | kibwen wrote:
               | _> Parsing isn 't too bad compared to, say, Perl._
               | 
               | This is damning with faint praise. Perl is _undecidable_
               | to parse! Even if C isn 't as bad as Perl, it's still bad
               | enough that there's an entire Wikipedia article devoted
               | to how bad it is:
               | https://en.wikipedia.org/wiki/Lexer_hack
        
             | grandempire wrote:
             | Each of these elements is even worse in every other
             | language I can think of. What language do you think is
             | simple in comparison?
        
               | mort96 wrote:
               | Go, Rust, Zig?
               | 
               | I'm curious, what language do you know of with a more
               | complex macro system than the whole C preprocessor?
               | 
               | EDIT: To be clear to prospective downvoters, I'm not just
               | throwing these languages out because they're hype or
               | whatever. They all have a grammar that's much simpler to
               | parse. Notably, you can construct a parse tree without a
               | semantic analyser which is capable of running in lockstep
               | with the parser to provide semantic information to the
               | parser. You can just write a parser which makes a parse
               | tree.
        
               | unclad5968 wrote:
               | I've never written a parser for any of those languages
               | but my intuition is that Go is easier to parse than C.
               | The others are debatable. Rust macros are definitely not
               | simpler than C macros. I'm not sure what could be simpler
               | than text substition. Zig doesn't have macros and
               | comptime is implemented as a language VM that runs as a
               | compilation step(last I knew), so that's definitely not
               | simpler. I don't use go often, but I don't think it has
               | macros at all so that's definitely simpler.
               | 
               | When people say that C is a simple language, my
               | interpretation is that they mean it is easy to interpret
               | what a C program does at a low level, not that it is
               | simple to write.
        
               | mort96 wrote:
               | The other languages can be written by a parser. A parser
               | for C needs a semantic analyzer working in tandem.
               | 
               | The C preprocessor is not text substitution.
               | 
               | It is not easy to describe what C does at a low level.
               | There are simple, easy to describe _and wrong_ models of
               | what C does  "at a low level". C's semantics are defined
               | by a very difficult to understand standards document, and
               | if you use one of those simple and enticing mental
               | models, you will end up with incorrect C code which works
               | until you try a different compiler or enable
               | optimisations.
        
         | ioma8 wrote:
         | I would say rust. When you learn the basics, rust is very
         | simple and will point to you any errors you have, so you get
         | basically no runtime errors. Also the type system is extremely
         | clean, making the code very readable.
         | 
         | But also C itself is very simple language. I do not mean C++,
         | but pure C. I would probably start with this. Yes, you will
         | crash at runtime errors, but besides that its very very simple
         | language, which will give you good understanding of memory
         | allocation, pointers etc.
        
           | ForOldHack wrote:
           | Got through C and K&R with no runtime errors, on four
           | platforms, but the first platform... Someone asked the
           | teacher why a struct would not work on Lattice C. The
           | instructor looked at the code, sat down at the students
           | computer, typed in a small program compiled it, and camly put
           | the disks in the box with the manual and threw it in the
           | garbage. "We will have a new compiler next week." We switched
           | to Manx C, which is what we had on the Amiga. Structs worked
           | on MS C, which I thought was the lettuce compiler. (
           | Apparently a different fork of the portable C compiler, but
           | later they admitted that it was still bigendian years later )
           | 
           | Best programming joke. Teacher said when your code becomes
           | "recalcitrent", we had no idea what he meant. This was in the
           | bottom floor of the library, so on break, we went upstairs
           | and used the dictionary. Recalcitrant means not obeying
           | authority. We laughed out loud, and then went silent. Opps.
           | 
           | The instructor was a commentator on the cryptic-C challenges,
           | and would often say... "That will not do what you think it
           | will do" and then go on and explain why. Wow. We learned a
           | _lot_ about the pre-processor, and more about how to write
           | clean and useful code.
        
         | steveklabnik wrote:
         | The reality is, the only languages that are truly simple are
         | Turing tarpits, like Brainfuck.
         | 
         | Reality is not simple. Every language that's used for real work
         | has to deal with reality. It's about how the language helps you
         | manage complexity, not how complex the language is.
         | 
         | Maybe Forth gets a pass but there's good reason why it's
         | effectively used in very limited circumstances.
        
         | 1wd wrote:
         | Oberon?
        
           | 90s_dev wrote:
           | I did not realize how intrinsically I also meant simple in
           | syntax until you suggested that.
        
         | timewizard wrote:
         | > but in practice it's actually very complex with tons of
         | nuance
         | 
         | That's because computers are very complex with tons of nuance.
        
         | fads_go wrote:
         | Lisp is build from a few simple axioms. Would that make it
         | simple?
        
           | dwattttt wrote:
           | So are entire branches of mathematics, and I feel safe in
           | saying they are not "simple"
        
       | tempodox wrote:
       | Highly interesting!                 main(argc, argv)       int
       | argv[];
       | 
       | This is a culture shock. Did the PDP-11 not distinguish between
       | `char` and `int`?
        
         | spijdar wrote:
         | Of course it did -- this was one of the distinguishing features
         | (byte addressing) of the PDP-11 vs the original machine that
         | ran UNIX, the PDP-7, after all ;-)
         | 
         | In "ancient"/K&R C, types weren't specified with the
         | parameters, but on the following lines afterwards. GCC would
         | still compile code like this, if passed the -traditional flag,
         | until ... some point in the last decade or so. Still, this
         | style was deprecated with ANSI C/C89, so it had a good run.
        
         | jecel wrote:
         | Here the `int` is being used in place of `char *`, not `char`.
         | 
         | And yes, the PDP-11 did have byte addressing while the PDP-7 on
         | which Unix was originally created was a word addressed machine.
        
           | tempodox wrote:
           | I see, now it makes sense.
        
       | ZhiqiangWang wrote:
       | Can't stop thinking about Ken Thompson Hack. This should be a
       | clean one ...
        
       | ricardo81 wrote:
       | Reminds me of the humility every programmer should have,
       | basically we're standing on the shoulders of giants and
       | abstraction for the most part. 80+ years of computer science.
       | 
       | Cool kids may talk about memory safety but ultimately someone had
       | to take care of it, either in their code or abstracted out of it.
        
         | wang_li wrote:
         | Big non sequitur, but your comment triggered a peeve of mine
         | that I find it ironic when people talk like oldsters can't
         | understand technology.
        
           | ricardo81 wrote:
           | Your comment sounds like a riddle. I've programmed for 25
           | years but appreciate there's a lot more going on than what I
           | know.
        
             | wang_li wrote:
             | Upon my own rereading, it is unclear. My point is that the
             | languages most of us use and the fundamental technologies
             | in the oses we use were designed/invented by people who are
             | in their 80s now, many of the Linux core team are 50-60.
        
           | worik wrote:
           | > ...people talk like oldsters can't understand technology
           | 
           | IMO it is young people that have trouble understanding.
           | 
           | The same mistakes are made over and over, lessons learned
           | long ago are ignored in the present
           | 
           | It easier to write than read, easier to talk than listen,
           | build new than expand the old
        
             | bigstrat2003 wrote:
             | This is the way of young people in every domain, not just
             | technology. Much like teenagers think they're the first
             | ones to ever have sex before, young people tend to think
             | they are the first ones to notice "hey this status quo
             | really sucks" and try to solve it.
             | 
             | This can be a strength, to be fair - the human mind really
             | _does_ tend to get stuck in a rut based on familiarity, and
             | someone new to the domain can spot solutions that others
             | haven 't because of that. But more often, it turns into
             | futile attempts to solve problems while forgetting the
             | lessons of the past.
        
           | phito wrote:
           | Understanding one level of abstraction doesn't mean you
           | understand the levels of abstraction built on top of it. And
           | vice versa.
        
         | estebank wrote:
         | The "cool kids talking about memory safety" are indeed standing
         | on the shoulders of giants, to allow for others to stand even
         | taller.
        
       | deweywsu wrote:
       | Am I interpreting this repo correctly? The first C compiler was
       | written in...C?
        
         | schindlabua wrote:
         | It would have been bootstrapped in assembly (or B/BCPL?) and
         | then once you can compile enough C to write a C compiler you
         | rewrite your compiler in C.
         | 
         | I remember a Computerphile video where prof. Brailsford said
         | something along the lines of "nobody knew who wrote the first C
         | compiler, everybody just kinda had it and passed it around the
         | office" which I think is funny. There's some sort of analogy to
         | life and things emerging from the primordial soup there, if you
         | squint hard enough.
        
         | froh wrote:
         | yes. the Q you're asking is: "how was this bootstrapped?"
         | 
         | the page that's referenced from GitHub doesn't describe that
         | 
         | http://cm.bell-labs.co/who/dmr/primevalC.html
         | 
         | however there probably was a running c compiler (written in
         | assembly) and an assembler and a linker available, hand
         | bootstrapped from machine code, then assembler, linker, then B,
         | NB and then C...
         | 
         | We can't tell but that would make sense...
        
         | xenadu02 wrote:
         | The first B compiler was written in BCPL on the GE 635
         | mainframe. Thompson wrote a B compiler in BCPL which they used
         | to cross-compile for PDP-7. Then Thompson rewrote B in B, using
         | the BCPL compiler to bootstrap. AFAIK this is the only clean
         | "bootstrap" step involved in the birth of C (BCPL -> B -> self-
         | compiled B)
         | 
         | Then they tweaked the compiler and called it NB (New B), then
         | eventually tweaked it enough they decided to call it C.
         | 
         | The compiler continuously evolved by compiling new versions of
         | itself through the B -> New B -> C transition. There was no
         | clean cutoff to say "ah this was the first C compiler written
         | in New B".
         | 
         | You can see evidence of this in the "pre-struct" version of the
         | compiler after Ritchie had added structure support but before
         | the compiler itself actually used structs. They compiled that
         | version of the compiler then modified the compiler source to
         | use structs, thus all older versions of the compiler could no
         | longer compile the compiler:
         | https://web.archive.org/web/20140708222735/http://thechangel...
         | 
         | Primeval C:
         | https://web.archive.org/web/20140910102704/http://cm.bell-la...
         | 
         | A modern bootstrapping compiler usually keeps around one or
         | more "simplified" versions of the compiler's source. The
         | simplest one either starts with C or assembly. Phase 0 is
         | compiled or assembled then is used to compile Phase 1, which is
         | used to compile Phase 2.
         | 
         | (Technically if you parsed through all the backup tapes and
         | restored the right versions of old compilers and compiler
         | source you'd have the bootstrap chain for C but no one bothered
         | to do that until decades later).
        
       | ramon156 wrote:
       | Love how unserious some of the code comments are. Makes you feel
       | less noob for a second :')
        
       | robertkoss wrote:
       | As someone who has no touchpoints with lower languages at all,
       | can you explain to me why those files are called c01, c02 etc.?
        
       | higgins wrote:
       | disappointed this didn't link to some analysis of clay tablets
        
       | sbassi wrote:
       | which compiler is used to compile the first compiler?
        
         | sc68cal wrote:
         | With BCPL
         | 
         | https://web.archive.org/web/20250130134200/https://www.bell-...
         | 
         | See also this comment
         | https://news.ycombinator.com/item?id=43462794
        
       | canucker2016 wrote:
       | Can people who have used/were around at this time (early 1970s)
       | give a description of the typical dev environment?
       | 
       | Also helpful: C history
       | https://en.wikipedia.org/wiki/C_language#History
       | 
       | From wikipedia, early Unix was developed on PDP/11 (16-bit).
       | 
       | signed 16-bit ints, 8-bit chars, arrays of those previous types.
       | 
       | identifiers were limited in length? (I'm seeing 8 chars,
       | lowercase, as the longest)
       | 
       | octal numeric constants, was hexadecimal used?
       | 
       | there was only a line editor available (vi was 1976)
       | 
       | did the file system support directories at that point?
       | 
       | no C preprocessor, no header files. (1973)
       | 
       | no make/makefiles (1976)
       | 
       | was there a std library used with the linker or an archive of
       | object files that was the 'standard' library?
       | 
       | Bourne shell wasn't around (1979), so wikipedia seems to point to
       | the Thompson shell - https://en.wikipedia.org/wiki/Thompson_shell
       | 
       | was there a debugger or was printf the only tool?
        
       ___________________________________________________________________
       (page generated 2025-03-24 23:00 UTC)