5 ms·
Compilers used to be my favorite subject in CS (circa 1975) and back then I never saw a single case where lexing was handled in the parser. A few factors back t
by todd8 3y ago
Compilers used to be my favorite subject in CS (circa 1975) and back then I never saw a single case where lexing was handled in the parser. A few factors back then may have been responsible for this.
First memory was very limited by todays standards, say 30 to 60kb. By making lexing a separate pass, the source could be discarded before the parser started and the tokenized intermediate file written by the lexer could be read during the parsing pass. Typically, the compiler might keep the name table of all identifiers in memory between these passes.
Around the mid 70s, languages that could be compiled in a single pass were investigated. Pascal was one of these. Pascal didn’t support separate compilation either so the linking step was eliminated. Nevertheless, the early Pascal compilers still did lexing separate from parsing; I learned Pascal by studying the source for Wirth’s compiler (it had crazy inconsistent indenting).
The second reason lexing was done separately was performance. Touching every character of the input source file was a major bottleneck for compilers back then, so optimizing the lexer was perceived to be very important. By doing the lexing in a tight loop rather than being called once for ever token lots of overhead associated with these calls was eliminated.
Also, there was a questionable attraction to bottom up parsing. Knuth had shown how LR (left to right) shift-reduce parsers could parse a very large family of grammars efficiently around 1965. Everyone was enamored with them. I even wrote a set of FORTRAN programs that would construct the SLR tables suitable for a subset of LR parsable grammars. When lex and yacc applications came along, everyone thought that every compiler should be built this way (to be fair, Wirth and Per Brinch Hansen were both designing languages that could easily be parsed by recursive descent because their grammars were LL(1) a smaller family of grammars than LR(n)). I’ve never seen anyone try to use only a LR parser at the lexical level combined with the normal grammar parsing.
I went back to University for another graduate degree in 1984, and I was surprised to hear the professor that taught the compiler class say that everyone should be using lex and yacc for any compiler development. By then, in the real world, people had discovered that much more meaningful error messages were possible with LL or recursive descent (i.e. top down) parsing.
Now, performance and memory considerations are different. It’s practical for compilers to read an entire source file in a single read (or memory map the entire source file), saving all the round trips to the OS for reading input. Top down parsing provides better error messages and it fits well with parsing all the way down to the lexiems.
- alaaalawi 3y agothis may be interseting (YMMV) read page 50 of "Compiler Construction for Digital Computers" 1971 which lists other reason some which are still valid and good to know
- lemming 3y agoBy then, in the real world, people had discovered that much more meaningful error messages were possible with LL or recursive descent (i.e. top down) parsing. This is largely orthogonal to whether you separate lexing out, though - it's perfectly possible (and very pleasant) to use recursive descent parsing over a token stream rather than a character stream.
- todd8 3y agoYes absolutely, I was just pointing out that there were in those days people still committed to bottom up parsing despite the advantages of top down approaches. The bottom up parsing with LR parsers that I was familiar with always used a table driven approach for the language grammar and assumed a separate lexer, sometimes hand written and sometimes generated by a program like lex. To me, the effort saved by using lex and yacc was completely lost by having to incorporate extra work to get good error messages for users out of LR parsers. Recursive descent over a character stream might have a negative performance impact, but I’m not sure of this considering the simple grammar involved in the lexical portion of the overall grammar and the ability of today’s tools to optimize function calls via inclining etc. If I was writing a compiler today for modern hardware, I would like to try using recursive descent on a single grammar that went all the down to the characters.
- Inviz 3y agoI saw source of pascal parser which was crazy beautiful and simple, and couldnt find it since. Does anybody know something like that?
- microtherion 3y agoNiklaus Wirth had a very readable and concise text about parsing Pascal family languages (originally Pascal style, eventually evolving to an Oberon dialect): https://people.inf.ethz.ch/wirth/CompilerConstruction/CompilerConstruction1.pdf https://people.inf.ethz.ch/wirth/CompilerConstruction/Compil...