Stack Machine Applications - Automata and Complexity Theory - Lecture Slides, Slides of Theory of Automata

Some concept of Automata and Complexity Theory are Administrivia, Closure Properties, Context-Free Grammars, Decision Properties, Deterministic Finite Automata, Intractable Problems, More Undecidable Problems. Main points of this lecture are: Stack Machine Applications, Central, Programming Language System, Compile a Program, Fundamentally, Top-Down Parsing, Recursive Descent Parsing, Bottom-Up Parsing, Compile a Program, Syntactically

Typology: Slides

2012/2013

Uploaded on 04/29/2013

shamir_69
shamir_69 🇮🇳

5

(4)

65 documents

1 / 40

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
Chapter Fifteen:
Stack Machine Applications
Docsity.com
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22
pf23
pf24
pf25
pf26
pf27
pf28

Partial preview of the text

Download Stack Machine Applications - Automata and Complexity Theory - Lecture Slides and more Slides Theory of Automata in PDF only on Docsity!

Chapter Fifteen:

Stack Machine Applications

The parse tree (or a simplified version called the abstract syntax tree)

is one of the central data structures of almost every compiler or other

programming language system. To parse a program is to find a parse

tree for it. Every time you compile a program, the compiler must

first parse it. Parsing algorithms are fundamentally related to stack

machines, as this chapter illustrates.

Parsing

• To parse is to find a parse tree in a given

grammar for a given string

• An important early task for every compiler

• To compile a program, first find a parse tree

  • That shows the program is syntactically legal
  • And shows the program's structure, which begins

to tell us something about its semantics

• Good parsing algorithms are critical

• Given a grammar, build a parser…

CFG to Stack Machine, Review

  • Two types of moves:
    1. A move for each production Xy
    2. A move for each terminal a ∈ Σ
  • The first type lets it do any derivation
  • The second matches the derived string and the input
  • Their execution is interlaced:
    • type 1 when the top symbol is nonterminal
    • type 2 when the top symbol is terminal

read pop push

X y

a a

Almost Deterministic

  • Not deterministic, but move is easy to choose
  • For example, abbcbba has three possible first moves, but only one makes sense:

S → aSa | bSb | c

read pop push

1. S aSa

2. S bSb

3. S c

4. a a

5. b b

6. c c

( abbcbba , S ) ↦ 1 ( abbcbba , aSa ) ↦ …

( abbcbba , S ) ↦ 2 ( abbcbba , bSb ) ↦ …

( abbcbba , S ) ↦ 3 ( abbcbba , c ) ↦ …

Lookahead

  • To decide among the first three moves:
    • Use move 1 when the top is S, next input a
    • Use move 2 when the top is S, next input b
    • Use move 3 when the top is S , next input c
  • Choose next move by peeking at next input symbol
  • One symbol of lookahead lets us parse this

deterministically

read pop push

1. S aSa

2. S bSb

3. S c

4. a a

5. b b

6. c c

S → aSa | bSb | c

  1. void predictiveParse(table, S) {
  2. initialize a stack containing just S
  3. while (the stack is not empty) {
  4. A = the top symbol on stack;
  5. c = the current symbol in input (or $ at the end)
  6. if (A is a terminal symbol) {
  7. if (A != c) the parse fails;
  8. pop A and advance input to the next symbol;
  9. }
  10. else {
  11. if table[A][c] is empty the parse fails;
  12. pop A and push the right-hand side of table[A][c];
  13. }
  14. }
  15. if input is not finished the parse fails
  16. }

The Catch

• To parse this way requires a parse table

• That is, the choice of productions to use at

any point must be uniquely determined by the

nonterminal and one symbol of lookahead

• Such tables can be constructed for some

grammars, but not all

LL(1) Grammars And Languages

• LL(1) grammars are those for which LL(1)

parsing is possible

• LL(1) languages are those with LL(1)

grammars

• There is an algorithm for constructing the

LL(1) parse table for a given LL(1) grammar

• LL(1) grammars can be constructed for most

programming languages, but they are not

always pretty…

Not LL(1)

• This grammar for a little language of

expressions is not LL(1)

• For one thing, it is ambiguous

• No ambiguous grammar is LL(1)

S → ( S ) | S+S | S*S | a | b | c

LL(1), But Ugly

  • Same language, now with an LL(1) grammar
  • Parse table is not obvious:
    • When would you use SAR?
    • When would you use B → ε?

S → AR

R → +AR | ε

A → XB

B → *XB | ε

X → ( S ) | a | b | c

a b c + * ( ) $ S SAR SAR SAR SAR R R+AR RRA AXB AXB AXB AXB B BB*XB BBX Xa Xb Xc X → ( S )

Outline

• 15.1 Top-Down Parsing

• 15.2 Recursive Descent Parsing

• 15.3 Bottom-Up Parsing

• 15.4 PDAs, DPDAs, and DCFLs

S → aSa | bSb | c

• Still chooses move using 1 lookahead symbol

• But parse table is incorporated into the code

void parse_S() { c = the current symbol in input (or $ at the end) if (c=='a') { // production SaSa match('a'); parse_S(); match('a'); } else if (c=='b') { // production SbSb match('b'); parse_S(); match('b'); } else if (c=='c') { // production Sc match('c'); } else the parse fails; }

Recursive Descent Structure

  • A function for each nonterminal, with a case for each

production:

  • For each RHS, a call to match each terminal, and a

recursive call for each nonterminal:

if (c=='a') { // production S → aSa

match('a'); parse_S(); match('a');

void match(x) {

c = the current symbol in input

if (c!=x) the parse fails;

advance input to the next symbol;