cs 330 programming languages 09 / 26 / 2006 instructor: michael eckmann

35
CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Upload: wilfred-mosley

Post on 12-Jan-2016

219 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

CS 330Programming Languages

09 / 26 / 2006

Instructor: Michael Eckmann

Page 2: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

Today’s Topics• Questions / comments?• grammars and FSM examples• Chapter 4

– Lexical analyzers– Parsers

• Top down, recursive descent

Page 3: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

FSM example

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• Consider a language that consists of all strings that only contain a's and b's AND end with the characters ab.

• Example sentences in this language are:ab, aaaab, bbbbab, babbabab, ...

• Help me write a grammar for this language.• Then we'll create a finite state automaton (FSA)

(aka finite state machine FSM) for this same language.

Page 4: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Lexical & Syntax Analysis

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• The syntax analysis portion of a language processor nearly always consists of two parts:– A low-level part called a lexical analyzer

(mathematically, a finite automaton based on a regular grammar)

– A high-level part called a syntax analyzer, or parser (mathematically, a push-down automaton based on a context-free grammar, or BNF)

• The parser can be based directly on the BNF

• Parsers based on BNF are easy to maintain

Page 5: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Lexical Analysis

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• Lexical analyzers are finite automatons (meaningful to you if you've taken MC 306 Theory of Computation)

• A finite automaton is a 5-tuple (Q, Sigma, q0, delta, A) where

– Q is a finite set of states

– Sigma is a finite set of input symbols

– q0 (the start state) is an element of Q

– delta (the transition function) is a function from QxSigma to Q

– A (the accepting states) is a subset of Q

Page 6: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Lexical Analysis

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

Page 7: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Lexical Analysis

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• Utility subprograms:–getChar - gets the next character of input, puts it in nextChar, determines its class and puts the class in charClass

–addChar - puts the character from nextChar into the place the lexeme is being accumulated, lexeme

– lookup - determines whether the string in lexeme is a reserved word (returns a code)

Page 8: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Lexical Analysis

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• Implementation:int lex() { getChar(); switch (charClass) { case LETTER: addChar(); getChar(); while (charClass == LETTER || charClass == DIGIT) { addChar(); getChar(); } return lookup(lexeme); break;

Page 9: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Lexical Analysis

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

…case DIGIT:

addChar();

getChar();

while (charClass == DIGIT) {

addChar();

getChar();

}

return INT_LIT;

break;

} /* End of switch */

} /* End of function lex */

Page 10: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Lexical Analysis

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• Lexical analyzers often construct the symbol table for the rest of the compiler to use.

• The symbol table consists of user-defined names.• The symbol table also consists of attributes of the names

(e.g. their type and other information) that is put there by some other step usually after the lexical analyzer creates the table. (Recall the flow chart diagram from chapter 1 that specified all the parts of the compilation/interpretation process.)

Page 11: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Parsers

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• Before we get into parsing, we need to remember what leftmost derivations and rightmost derivations (I don't think I defined rightmost derivations yet) are.

• Anyone care to explain?

• Any recollection from chapter 3 what a sentential form is?

• Pop quiz:

• What's the purpose of the lexical analyzer again?

Page 12: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Parsers

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• Goals of the parser, given an input program:– Find all syntax errors; for each, produce an appropriate

diagnostic message, and recover quickly --- that is be able to find as many syntax errors in the same pass through the code (is this easy?)

– Produce the parse tree, or at least a trace of the parse tree, for the program

• Two categories of parsers

– Top down parsers – build the tree from the root down to the leaves

– Bottom up parsers – build the tree from the leaves upwards to the root.

Page 13: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Parsers

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• Top down - produce the parse tree, beginning at the root– Order is that of a leftmost derivation

• Bottom up - produce the parse tree, beginning at the leaves– Order is that of the reverse of a rightmost derivation

• The parsers we'll be dealing with look only one token ahead in the input– Can anyone guess what this means?

Page 14: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Top Down Parsers

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• OK, as input we have a possible sentence (program) in the language that may or may not be syntactically correct.

• The lexical analyzer is called by the parser to get the next lexeme and associated token code from the input sentence (program.)

Page 15: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Top Down Parsers

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• A step in the life of a top down parser...

• Think of this now as during some arbitrary step in the top down parser we have a sentential form that is part of a leftmost derivation, the goal is to find the next sentential form in that leftmost derivation.

• e.g. Given a left sentential form xA – where x is a string of terminals– A is a non-terminal– is a string of terminals and non-terminals

• Since we want a leftmost derivation, what would we do next?

Page 16: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Top Down Parsers

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• We have a left sentential form xA (some string in the derivation)

• Expand A (the leftmost non-terminal) using a production that has A as its LHS.

• How might we do this?

Page 17: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Top Down Parsers

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• We have a left sentential form xA

• Expand A (the leftmost non-terminal) using a production that has A as its LHS.

• How might we do this?– We would call the lexical analyzer to get the next lexeme.

– Then determine which production to expand based on the lexeme/token returned.

– For grammars that are “one lookahead”, exactly one RHS of A will have as its first symbol the lexeme/token returned. If there is no RHS of A with its first symbol being the lexeme/token returned --- what do we think occurred?

Page 18: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Top Down Parsers

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• Top down parsers can be implemented as recursive descent parsers --- that is, a program based directly on the BNF description of the language.

• Or they can be represented by a parsing table that contains all the information in the BNF rules in table form.

• These top down parsers are called LL algorithms --- L for left-to-right scan of the input and L for leftmost derivation.

Page 19: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Top Down Parsers

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• Left recursion is problematic for top-down parsers.

• e.g. <A> -> <A> b

• Each non-terminal is written as a function in the parser. So, a recursive descent parser function for this production would continually call itself first and never get out of the recursion. Similarly for any top-down parser.

• Left recursion is problematic for top-down parsers even if it is indirect.

• e.g. <A> -> <B> b

<B> -> <A> a

• Bottom up parsers do not have this problem with left recursion.

Page 20: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Top Down Parsers

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• When a nonterminal has more than one RHS, the first terminal symbol that can be generated in a derivation for each of them must be unique to that RHS. If it is not unique, then top-down parsing with one lookahead is impossible for that grammar.

• Because there is only one lookahead symbol, which lex would return to the parser, that one symbol has to uniquely determine which RHS to choose.

• Does that make sense?

Page 21: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Top Down Parsers

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• Example:

• Here are two productions in a possible grammar:

• <variable> -> identifier | identifier [ <expression> ]

• Assuming we have a left sentential form where the next nonterminal to be expanded is <variable> and we call out to lex() to get the next token and it returns identifier. Which production will we pick?

Page 22: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Top Down Parsers

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• The pairwise disjointness test is used on grammars to determine if they have this problem. What it does is, it determines the first symbol for every RHS of a production and for those with the same LHS, the first symbols must be different for each.

• <A> -> a <B> | b<A>b | c <C> | d

• The 4 RHSs of A here each have as their first symbol {a}, {b}, {c}, {d} respectively. These sets are all disjoint so these productions would pass the test. The ones on the previous slide would not.

• Note the use of curly braces on this slide is NOT the EBNF usage but the standard set usage of braces.

Page 23: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Top Down Parsers

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• Note: if the first symbol on the RHS is a nonterminal, we would need to expand it to determine the first symbol.

• e.g.

• <A> -> <B> a | b<A>b | c <C> | d

• <B> -> <C> f

• <C> -> s <F>

• Here, to determine the first symbol of the first RHS of A, we need to follow <B> then <C> to find out that it is s. So, the 4 RHSs of A here each have as their first symbol {s}, {b}, {c}, {d} respectively. These sets are all disjoint so these productions would pass the test.

Page 24: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Recursive descent example

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• So called because many of its subroutines/functions (one for each non-terminal) are recursive and descent because it is a top-down parser.

• EBNF is well suited for these parsers because EBNF reduces the number of non-terminals (as compared to plain BNF.)

• A grammar for simple expressions:<expr> <term> {(+ | -) <term>}<term> <factor> {(* | /) <factor>}<factor> id | ( <expr> )

• Anyone remember what the curly braces mean in EBNF?

Page 25: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Recursive descent example

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• Assume we have a lexical analyzer named lex, which puts the next token code in nextToken

• The coding process when there is only one RHS:– For each terminal symbol in the RHS, compare it

with the next input token; if they match, continue, else there is an error

– For each nonterminal symbol in the RHS, call its associated parsing subprogram

Page 26: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Recursive descent example

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

/* Function expr (written in the C language) Parses strings in the language generated by the rule: <expr> → <term> {(+ | -) <term>} */

void expr() {

/* Parse the first term */

term(); /* term() is it's own function representing the non-terminal <term>*/

/* As long as the next token is + or -, call lex to get the next token, and parse the next term */   while (nextToken == PLUS_CODE || nextToken == MINUS_CODE) {     lex();     term();   } }

Page 27: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Recursive descent example

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• Note: by convention each function leaves the most recent token in nextToken

• A nonterminal that has more than one RHS requires an initial process to determine which RHS it is to parse– The correct RHS is chosen on the basis of the next

token of input (the lookahead)– The next token is compared with the first token that

can be generated by each RHS until a match is found– If no match is found, it is a syntax error

• Let's now look at an example of how the parser would handle multiple RHS's.

Page 28: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Recursive descent example

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

/* Function factor Parses strings in the language generated by the rule: <factor> -> id | (<expr>) */ void factor() {  if (nextToken) == ID_CODE) /*Determine which RHS*/

/* For the RHS id, just call lex */     lex();

/* If the RHS is (<expr>) – call lex to pass over the leftparenthesis, call expr, and check for the right parenthesis */

   else if (nextToken == LEFT_PAREN_CODE) { lex(); expr(); if (nextToken == RIGHT_PAREN_CODE) lex(); else error(); } /* End of else if (nextToken == ... */ else error(); /* Neither RHS matches */

}

Page 29: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Recursive descent example

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• The term function for the <term> non-terminal will be similar to the expr function for the <expr> non-terminal. See the text for this function.

Page 30: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Bottom Up parsers

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• Bottom-up parsers– Given a right sentential form, , determine what

substring of is the right-hand side of the rule in the grammar that must be reduced to produce the previous sentential form in the right derivation

– This substring is called the handle– This is backwards from the way top down parsing

works– The most common bottom-up parsing algorithms are

in the LR family (L=left-to-right scan of input, R= rightmost derivations)

Page 31: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Bottom Up parsers

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• Bottom-up parsers– The whole process is: Starting with the input sentence

(all terminal symbols) (also this is the last sentential form in a derivation), produce the sequence of sentential forms (up) until all that remains is the start symbol.

– One step in the process of bottom-up parsing is: given a right sentential form, find the correct RHS to reduce to get the previous right-sentential form in the derivation

– What are we reducing a RHS to?

Page 32: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Bottom Up parsers

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

Example:E -> E + T | TT -> T * F | FF -> ( E ) | id

E => E + T => E + T * F => E + T * id => E + F * id => E + id * id => T + id * id => F + id * id => id + id * id Read from the bottom going up.

Remember --- we start with the

sentence (in this case id + id + id.)

The underlined symbol is the one

that gets reduced.

Page 33: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Bottom Up parsers

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

Example:

E -> E + T | T

T -> T * F | F

F -> ( E ) | id

E => E + T

=> E + T * F

=> E + T * id

=> E + F * id

=> E + id * id

=> T + id * id

=> F + id * id

=> id + id * id

• Note: Sometimes it is not obvious which RHS to reduce in each step. For instance, the right-sentential form E + T * id includes more than one RHS. Which RHSs are included in this sentential form?

• And why can we not choose the others?

Page 34: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Bottom Up parsers

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• The correct choice of the RHS to reduce in any given step is called the handle (of that right-sentential form.)

• The text contains intuition on how to find the handle --- we won't cover that here. Just know that the handle of any right-sentential form is unique.

• To decide what part of the rightmost sentential form is the handle, one should understand what a phrase is and what a simple phrase is. Then the handle is defined to be the leftmost simple phrase within a right sentential form.

Page 35: CS 330 Programming Languages 09 / 26 / 2006 Instructor: Michael Eckmann

Bottom Up parsers

Michael Eckmann - Skidmore College - CS 330 - Fall 2006

• Most bottom-up parsers are LR --- what is LR again?

• They use small programs and a parsing table.