and
This is the approach you are required to use.
1. <start> --> <expression> eof 2. <expression> --> <term> <expression_tail> 3. <expression_tail> --> + <term> <expression_tail> 4. <expression_tail> --> - <term> <expression_tail> 5. <expression_tail> --> e 6. <term> --> <factor> <term_tail> 7. <term_tail> --> * <factor> <term_tail>> 8. <term_tail> --> / <factor> <term_tail> 9. <term_tail> --> e 10. <factor> --> <primary> <factor_tail> 11. <factor_tail> --> ^ <primary> <factor_tail> 12. <factor_tail> --> e 13. <primary> --> identifier 14. <primary> --> integer_literal 15. <primary> --> ( <expression> )
procedure term_tail isbegin case Lookahead is -- Lookahead is a global variable when ?? ==> -- 7. <term_tail> --> * <factor> <term_tail> Match('*'); Factor; Term_Tail; when ?? ==> -- 8. <term_tail> --> / <factor> <term_tail> Match('/'); Factor; Term_Tail; when ?? ==> -- 9. <term_tail> --> e null; when others ==> Error; end case; end term_tail;Here, the ?? are to be replaced by the appropriate tokens determined in the LL(1) table. For example, the first when clause is used to expand <term_tail> by rule number 7. So, we would look in the row of the LL(1) table labeled with <term_tail>, find every occurrence of rule number 7 in that row, and place the corresponding tokens that appear as the column headers in the LL(1) table in place of the ??. In this case, "*" would be the only token that predicted using rule 7.
This is a common approach used by professional compiler writers who are doing a hand-build compiler. (For consistency's sake, you are to use approach 1, not this one, in your compiler project.)
Consider the following four rules in the above grammar
6. <term> --> <factor> <term_tail> 7. <term_tail> --> * <factor> <term_tail>> 8. <term_tail> --> / <factor> <term_tail> 9. <term_tail> --> e
The EBNF for this might look like
term := factor { mulop factor }
A procedure built around this particular rule might look like
procedure term isbegin factor; while lookahead = "*" | "/" loop match; factor; end while; end term;
This approach still requires that one understand the how lookaheads drive the parse and thus still requires the construction of the LL(1) table. It isn't always so clear just how to build the parser directly from the EBNF and the LL(1) table, however.
A table driven parser works very much like a pushdown automaton. In fact, a pushdown automaton can be constructed quite easily to parse an LL(1) grammar. (Remember that for every context free grammar there is a pushdown automaton that recognizes the language of that grammar). To construct a table driven LL(1) parser we need:
Let's look at the LL(1) grammar for Micro, the toy programming language in the book:
1. <program> --> begin <statement list> end 2. <statement list> --> <statement> <statement tail> 3. <statement tail> --> <statement> <statement tail> 4. <statement tail> --> l 5. <statement> --> ID := <expression> ; 6. <statement> --> read ( <id list> ) ; 7. <statement> --> write ( <expr list> ) ; 8. <id list> --> id <id tail> 9. <id tail> --> , id <id tail> 10. <id tail> --> l 11. <expr list> --> <expression> <expr tail> 12. <expr tail> --> , <expression> <expr tail> 13. <expr tail> --> l 14. <expression> --> <primary> <primary tail> 15. <primary tail> --> <add op> <primary> <primary tail> 16. <primary tail> --> l 17. <primary> --> ( <expression> ) 18. <primary> --> ID 19. <primary> --> INTLIT 20. <add op> --> + 21. <add op> --> - 22. <system goal> --> <program> $
The LL(1) table for this grammar is
id |
intlit |
:= |
, |
; |
+ |
- |
( |
) |
begin |
end |
read |
write |
$ |
|
| <program> | 1 | |||||||||||||
| <statement_list> | 2 | 2 | 2 | |||||||||||
| <statement> | 5 | 6 | 6 | |||||||||||
| <statement_tail> | 3 | 4 | 3 | 3 | ||||||||||
| <expression> | 14 | 14 | 14 | |||||||||||
| <id_list> | 8 | |||||||||||||
| <expr_list> | 11 | 11 | 11 | |||||||||||
| <id_tail> | 9 | 10 | ||||||||||||
| <expr_tail> | 12 | 13 | ||||||||||||
| <primary> | 18 | 19 | 17 | |||||||||||
| <primary_tail> | 16 | 16 | 15 | 15 | 16 | |||||||||
| <add_op> | 20 | 20 | ||||||||||||
| <system_goal> | 22 |
Use the above grammar and LL(1) table to parse
begin A := Fred - 314 + A; end $
Do both a parse tree and a parse with a table driven method.
......
Having gone through an example of parsing using a stack and the LL(1) table we can now write a stack-based table driven top-down parser by hand. You will notice that the algorithm essentially implements a pushdown automaton.
push <system_goal> onto the stack get lookahead_token while the stack is not empty loop if the stack top is a nonterminal then rule_to_apply := LL_Table(stack_top, lookahead_token) if rule_to_apply /= null then pop stack push right hand side of rule_to_apply else error; end if else -- the stack top is a token match(stack_top); end if end whileif end of file then accept; -- the input string parses else reject; -- the input string does not parse (and therefore is not in the language end if;