One could argue that C's success is largely because it was even simpler than Pascal and more generic --- a notable example is that Pascal has I/O built-in to the language, while C defined them as part of the standard library (which could even not be present.)
From a compiler writer's perspective, C is much more complex than Wirth's Pascal.
Pascal's builtin I/O was a major impediment to its usability.
However, one really great feature of Wirth's Pascal is nested functions with access to outer scopes, which D wholeheartedly embraces. They really are slick. I use them heavily.
int moon(int s)
{
int sum(int x) { return s + x; }
return sum(3);
}
An extra parameter is passed to the nested function, called a "static link", which is a pointer to the stack frame of the statically enclosing function. This is in addition to the usual "dynamic link" which is a pointer to the stack frame that called the nested function.
Nested functions can also be nested, and can access variables in enclosing functions by walking the static links back.
The neato thing about this is it makes stack frames work exactly like members of a struct. In fact, a pointer to a nested function is analogous to (and binary interchangeable with) a pointer to a stack object.
My first (and only) serious compiler had such nested functions, though without stack frames. Instead my VM had two stacks: one for function arguments and locals, and the other for return addresses, same as Forth.
I had no stack frame at all. Instead, the compiler kept track of the stack offset of every accessible local (relative to the top of stack). That way the implementation of my nested function was kind of trivial: there were no difference between true locals like `x` and locals from an outer scope like `s`, except of course for the possibility of shadowing.
One reason this was not special is that internally, it was nested scopes all the way down: there was one scope per local variable, so merely declaring two variables already means dealing with nested scopes. Once that was out of the way adding nesting functions was really easy (I believe it added less than 20 lines to my compiler).
Nowadays I think I would use a single stack instead, but if my language is simple enough I’ll probably skip the frame pointer and just keep track of offsets like I did this first time.
if your outer function f called a function 'quicksort' which called a function 'partition' which called your nested function 'compare', how did 'compare' get access to the variables of f from its statically enclosing scope? how did it know how many locals 'quicksort' and 'partition' had pushed onto the operand stack?
well, that's the problem the static link solves; it's not such a difficult thing to implement if you let procedure values (function pointers) be two words instead of one. gcc emits trampoline code onto the stack at runtime to support this without breaking abi compatibility, so the pointer to the nested function actually points to the trampoline, which just supplies the static link and calls the real nested function
I get that the C syntax is very awkward, compared to Pascal's. Are there more reasons that make C compilers more complex than Pascal ones, and would you tell which, please? I'm curious.
> One could argue that C's success is largely because[...]
Why do so many ignore the obvious? Ergonomics matters. At a time when most programmers didn't touch type (even the big stars), the economy of being able to type `int foo(int bar) { ... }` is a major productivity boost in comparison to `PROCEDURE foo(VAR bar: INTEGER); BEGIN ... END foo;`. Hell, that even bothers modern programmers. I mean, I know people whose entire reason for switching to Chrome when it came out was that the toolbars at the top of the screen were 18 pixels shorter or whatever. That's it. People are incredibly capricious.
Factor in the environment—Unix has a widely acknowledged beauty/elegance in its design—and it's not hard to see why people gravitated to C, even at a time when Pascal was anointed as the official development language by Apple for a platform where people were known to be able to make money writing apps and selling them to businesses and consumers alike.