One of the reasons why Pre-Scheme was so lovely to use is because when it transpiled to C, the C was highly readable. You could follow the output of the Pre-Scheme compiler and know exactly what it was doing, provided you knew idiomatic C.
From a readability standpoint, the C output of Gambit and Chicken is a hot mess in comparison.
Icon's compiler used to generate somewhat readable C output. It was still the result of continuation passing style conversion (CPS) though, so it wasn't exactly easy to follow. Nowadays Icon only has an interpreter :(
If you want something like Icon or a Lisp or Scheme to generate readable C, you're going to have to use Simon Tatham's C co-routine scheme [0] (used by PuTTY) so that you can keep code looking sequential. You'd have to generalize the co-routine thing so it's not so much about co-routines but about closures (the two concepts are remarkably related). If you allocate call frames on the stack, then you get depth-first search and backtracking support, and closures of dynamic extent. If you allocate call frames on the heap then you get breadth-first search and backtracking, closures of indefinite extent, and co-routines.
(PuTTY has all of the SSHv1 and SSHv2 protocol up to the end of authentication coded as one enormous function each, and this reads surprisingly well in spite of being such enormous functions. That works for PuTTY because those protocols are extremely sequential up to the end of authentication. Think of Simon Tatham's co-routines a an await for C.)
I came to the same conclusions with my own compiler from perl to C. Lot of work went into generating perfectly readable and formatted C code, with all the whistles and ifdef's for debugging, tracing, Config and architecture optimizations. The good thing is that it's much easier in the compiler to do than in the resulting C code. We generate a lot of C code now, automatically, for perfect hash tables, optimized unicode tables, generating API's and exhaustive test cases and much more. It must be pretty and usable, gambit-c is a good example for throwaway code nobody wants to debug or read through.
Indeed. If you want to support full Scheme semantics, with continuations and all the crazy control structures, you really need to contort the C language into a form that's pretty hard to grok. That's the sacrifice Gambit and Chicken made, and it's probably a worthy one given how excellent and complete those compilers are.
Pre-Scheme is intended for a different use case -- when you want the level of fine-grained control that C gives you but don't want to leave Lisp behind. Its semantics are accordingly quite different from Scheme's and it doesn't support the full set of Scheme control structures, nor implicit garbage collection.
Yes, and Pre-Scheme deliberately avoids implementing all of Scheme in order to emit straightforward C.
Gambit and Chicken are fine programs -- close to best of breed in the Scheme->C transpiler space. Pre-Scheme was intended to map straightforwardly onto C semantics so that applications like the one for which it was designed -- implementing the Scheme48 VM -- could be coded and tested in a Lisp environment before being transpiled to understandable C code. They sacrificed full Scheme semantics for this.
I still remember the first C++ compiler we had that compiled down to C. The code was a total nightmare. I first had to debug the C code and then after finding the problem I had to somehow divine where in the C++ code it came from.
From a readability standpoint, the C output of Gambit and Chicken is a hot mess in comparison.