The common workaround for this kind of thing for other languages was to either write to disk in between multiple passes and/or load code for one pass at a time. We still have multiple passes in many compiler toolchains in the form of compiler->assembler->linker, but there were older compilers with ridiculous numbers of passes out of sheer necessity.
E.g. here is a HN discussion about a 63 pass compiler for the IBM 1401 [1]
(There's been a resurgence in interest in multiple passes in the form of "nanopass" architectures, but today that's driven out of a desire to simplify code, whereas before it used to be out of a need to minimize memory use or even fit the code needed in memory)
But, yes, this is one of the drivers in a lot of older languages, but particularly the Wirth-ian ones, for declarations before use, etc. so that the language can be easily parsed and compiled in a single pass. The Wirth compilers typically didn't build an AST either.
E.g. here is a HN discussion about a 63 pass compiler for the IBM 1401 [1]
(There's been a resurgence in interest in multiple passes in the form of "nanopass" architectures, but today that's driven out of a desire to simplify code, whereas before it used to be out of a need to minimize memory use or even fit the code needed in memory)
But, yes, this is one of the drivers in a lot of older languages, but particularly the Wirth-ian ones, for declarations before use, etc. so that the language can be easily parsed and compiled in a single pass. The Wirth compilers typically didn't build an AST either.
[1] https://news.ycombinator.com/item?id=9289070