• Home
  • Queen's University
  • Software Specifications
  • Parsing Techniques in Compiler Design

Parsing Techniques in Compiler Design

CISC/CMPE-223, Winter 2023, Parsing 1 Parsing This material is covered in Chapter 11 of the textbook. Parsing is the process of determining if a string of tokens can be generated by a grammar. As discussed in the previous weeks, context-free languages are recognized by pushdown automata. A parser, as discussed in this course, is a deterministic pushdown automaton. While a pushdown automaton just gives a "yes" or "no" answer of an input string, the parsing stage of a compiler actually constructs the parse tree and the parse tree is used in program translation. Parsing in an important step in the compilation of programming languages. There are two general approaches to do this: Ā· Attempt to construct reasonably efficient parsers for general context-free languages. Ā· Define subclasses of context-free grammars which can be parsed more efficiently. A straightforward "brute-force" parsing method for general context-free grammars sys- tematically tries all possible derivations that could produce the given terminal string. This is extremely inefficient (and for grammars with E-productions the straightforward parsing method does not even need to terminate). Using a dynamic programming technique we can get a significantly improved parsing algorithm for general context-free grammars, with time complexity O(n3). However, this is still not good enough for compilers which need to handle very large size programs. Parsing based on a deterministic pushdown automaton can be done in linear time.1 In the following we consider a subclass of grammars for which an efficient parser can be constructed in a straightforward way. Roughly speaking, a recursive descent parser associates 1Recall that some context-free languages cannot be recognized by a deterministic pushdown automaton. CISC/CMPE-223, Winter 2023, Parsing 2 a procedure to each nonterminal of the grammar. The parse tree is constructed top-down by recursively calling the procedure of the current left-most nonterminal. We consider a special case of recursive descent parsing, called predictive recursive descent. In predictive parsing the current input token (look-ahead symbol) uniquely determines the procedure chosen for the nonterminal, that is, the first token of the remaining input deter- mines the production chosen for the nonterminal. A recursive descent (predictive) parser does not explicitly construct the parse tree, al- though it does so implicitly. Example. Grammar for balanced strings: < balanced > -> < empty > | 0 < balanced > 1 < empty > -> € The recursive descent parser defines a function Balanced()that makes a recursive call: MustBe(ZERO) Balanced() MustBe(ONE) More details of the construction, including the code for the parser, can be found on page 229 in the textbook. Here "ZERO" and "ONE" are tokens corresponding to the input symbols 0 and 1, respectively. The function MustBe() advances to the next token if the argument matches the lookahead symbol. Consider the operation of the parser on the input: 000111 Initially we call procedure Balanced(), gettoken() returns ZERO and the switch-statement determines that we execute the sequence MustBe(ZERO); Balanced(); MustBe(ONE) CISC/CMPE-223, Winter 2023, Parsing 3 Now the symbol 0 is consumed by MustBe(ZERO) and then Balanced() is called again. Again