Download presentation
Presentation is loading. Please wait.
1
Syntax Analysis (Chapter 4) 1 Course Overview PART I: overview material 1Introduction 2Language processors (tombstone diagrams, bootstrapping) 3Architecture of a compiler PART II: inside a compiler 4Syntax analysis 5Contextual analysis 6Runtime organization 7Code generation PART III: conclusion 8Interpretation 9Review
2
Syntax Analysis (Chapter 4) 2 Systematic Development of Rec. Descent Parser (1)Express grammar in EBNF (2)Grammar Transformations: Left factorization and Left recursion elimination (3)Create a parser class with –private variable currentToken –methods to call the scanner: accept and acceptIt (4) Implement a public method for main function to call: –public parse method that fetches the first token from the scanner calls parse S (where S is start symbol of the grammar) verifies that scanner next produces the end–of–file token (5)Implement private parsing methods: –add private parse N method for each non terminal N
3
Syntax Analysis (Chapter 4) 3 Developing RD Parser for Mini Triangle Identifier := Letter (Letter|Digit)* Integer-Literal ::= Digit Digit* Operator ::= + | - | * | / | | = Comment ::= ! Graphic* eol Identifier := Letter (Letter|Digit)* Integer-Literal ::= Digit Digit* Operator ::= + | - | * | / | | = Comment ::= ! Graphic* eol Before we begin: The following non-terminals are recognized by the scanner They will be returned as tokens by the scanner Assume scanner returns instances of this class: public class Token { byte kind; String spelling; final static byte IDENTIFIER = 0, INTLITERAL = 1;... public class Token { byte kind; String spelling; final static byte IDENTIFIER = 0, INTLITERAL = 1;...
4
Syntax Analysis (Chapter 4) 4 (1)&(2) Developing RD Parser for Mini Triangle Program ::= single-Command Command ::= single-Command | Command ; single-Command single-Command ::= V-name := Expression | Identifier ( Expression ) | if Expression then single-Command else single-Command | while Expression do single-Command | let Declaration in single-Command | begin Command end V-name ::= Identifier... Program ::= single-Command Command ::= single-Command | Command ; single-Command single-Command ::= V-name := Expression | Identifier ( Expression ) | if Expression then single-Command else single-Command | while Expression do single-Command | let Declaration in single-Command | begin Command end V-name ::= Identifier... Left factorization needed Left recursion elimination needed
5
Syntax Analysis (Chapter 4) 5 (1)&(2) Express grammar in EBNF and transform Program ::= single-Command Command ::= single-Command (; single-Command)* single-Command ::= Identifier ( := Expression | ( Expression ) ) | if Expression then single-Command else single-Command | while Expression do single-Command | let Declaration in single-Command | begin Command end V-name ::= Identifier... Program ::= single-Command Command ::= single-Command (; single-Command)* single-Command ::= Identifier ( := Expression | ( Expression ) ) | if Expression then single-Command else single-Command | while Expression do single-Command | let Declaration in single-Command | begin Command end V-name ::= Identifier... After factorization etc. we get:
6
Syntax Analysis (Chapter 4) 6 (1)&(2) Developing RD Parser for Mini Triangle Expression ::= primary-Expression | Expression Operator primary-Expression primary-Expression ::= Integer-Literal | V-name | Operator primary-Expression | ( Expression ) Declaration ::= single-Declaration | Declaration ; single-Declaration single-Declaration ::= const Identifier ~ Expression | var Identifier : Type-denoter Type-denoter ::= Identifier Expression ::= primary-Expression | Expression Operator primary-Expression primary-Expression ::= Integer-Literal | V-name | Operator primary-Expression | ( Expression ) Declaration ::= single-Declaration | Declaration ; single-Declaration single-Declaration ::= const Identifier ~ Expression | var Identifier : Type-denoter Type-denoter ::= Identifier Left recursion elimination needed
7
Syntax Analysis (Chapter 4) 7 (1)&(2) Express grammar in EBNF and transform Expression ::= primary-Expression ( Operator primary-Expression )* primary-Expression ::= Integer-Literal | Identifier | Operator primary-Expression | ( Expression ) Declaration ::= single-Declaration (; single-Declaration)* single-Declaration ::= const Identifier ~ Expression | var Identifier : Type-denoter Type-denoter ::= Identifier Expression ::= primary-Expression ( Operator primary-Expression )* primary-Expression ::= Integer-Literal | Identifier | Operator primary-Expression | ( Expression ) Declaration ::= single-Declaration (; single-Declaration)* single-Declaration ::= const Identifier ~ Expression | var Identifier : Type-denoter Type-denoter ::= Identifier After factorization and recursion elimination :
8
Syntax Analysis (Chapter 4) 8 (3)&(4) Create a parser class and public parse method public class Parser { private Token currentToken; private void accept (byte expectedKind) { if (currentToken.kind == expectedKind) currentToken = scanner.scan( ); else report syntax error } private void acceptIt( ) { currentToken = scanner.scan( ); } public void parse( ) { acceptIt( ); // get the first token parseProgram( ); // Program is the start symbol if (currentToken.kind != Token.EOT) report syntax error }... public class Parser { private Token currentToken; private void accept (byte expectedKind) { if (currentToken.kind == expectedKind) currentToken = scanner.scan( ); else report syntax error } private void acceptIt( ) { currentToken = scanner.scan( ); } public void parse( ) { acceptIt( ); // get the first token parseProgram( ); // Program is the start symbol if (currentToken.kind != Token.EOT) report syntax error }...
9
Syntax Analysis (Chapter 4) 9 (5) Implement private parsing methods private void parseProgram( ) { parseSingleCommand( ); } private void parseProgram( ) { parseSingleCommand( ); } Program ::= single-Command
10
Syntax Analysis (Chapter 4) 10 (5) Implement private parsing methods single-Command ::= Identifier ( := Expression | ( Expression ) ) | if Expression then single-Command else single-Command |... other alternatives... single-Command ::= Identifier ( := Expression | ( Expression ) ) | if Expression then single-Command else single-Command |... other alternatives... private void parseSingleCommand( ) { switch (currentToken.kind) { case Token.IDENTIFIER :... case Token.IF :...... other cases... default: report a syntax error } private void parseSingleCommand( ) { switch (currentToken.kind) { case Token.IDENTIFIER :... case Token.IF :...... other cases... default: report a syntax error }
11
Syntax Analysis (Chapter 4) 11 (5) Implement private parsing methods single-Command ::= Identifier ( := Expression | ( Expression ) ) | if Expression then single-Command else single-Command | while Expression do single-Command | let Declaration in single-Command | begin Command end single-Command ::= Identifier ( := Expression | ( Expression ) ) | if Expression then single-Command else single-Command | while Expression do single-Command | let Declaration in single-Command | begin Command end From the above we can straightforwardly derive the entire implementation of parseSingleCommand (much as we did in the microEnglish example)
12
Syntax Analysis (Chapter 4) 12 Algorithm to convert EBNF into a RD parser private void parseN( ) { parse // as explained on next two slides } private void parseN( ) { parse // as explained on next two slides } N ::= The conversion of an EBNF specification into a Java or C ++ implementation for a recursive descent parser is so “mechanical” that it could easily be automated (such tools exist, but we won’t use them in this course) We can describe the algorithm by a set of mechanical rewrite rules
13
Syntax Analysis (Chapter 4) 13 Algorithm to convert EBNF into a RD parser // a dummy statement parse parse N where N is a non-terminal parseN( ); parse t where t is a terminal accept(t); parse X Y parse X parse Y parse X parse Y
14
Syntax Analysis (Chapter 4) 14 Algorithm to convert EBNF into a RD parser parse X* while (currentToken.kind is in starters[X]) { parse X } while (currentToken.kind is in starters[X]) { parse X } parse X | Y switch (currentToken.kind) { cases in starters[X]: parse X break; cases in starters[Y]: parse Y break; default: if neither X nor Y generates then report syntax error } switch (currentToken.kind) { cases in starters[X]: parse X break; cases in starters[Y]: parse Y break; default: if neither X nor Y generates then report syntax error }
15
Syntax Analysis (Chapter 4) 15 private void parseCommand( ) { parse single-Command ( ; single-Command )* } private void parseCommand( ) { parse single-Command ( ; single-Command )* } Example: “Generation” of parseCommand Command ::= single-Command ( ; single-Command )* private void parseCommand( ) { parse single-Command parse ( ; single-Command )* } private void parseCommand( ) { parse single-Command parse ( ; single-Command )* } private void parseCommand( ) { parseSingleCommand( ); parse ( ; single-Command )* } private void parseCommand( ) { parseSingleCommand( ); parse ( ; single-Command )* } private void parseCommand( ) { parseSingleCommand( ); while (currentToken.kind==Token.SEMICOLON) { parse ; single-Command } private void parseCommand( ) { parseSingleCommand( ); while (currentToken.kind==Token.SEMICOLON) { parse ; single-Command } private void parseCommand( ) { parseSingleCommand( ); while (currentToken.kind==Token.SEMICOLON) { parse ; parse single-Command } private void parseCommand( ) { parseSingleCommand( ); while (currentToken.kind==Token.SEMICOLON) { parse ; parse single-Command } private void parseCommand( ) { parseSingleCommand( ); while (currentToken.kind==Token.SEMICOLON) { acceptIt( );// because SEMICOLON has just been checked parseSingleCommand( ); } private void parseCommand( ) { parseSingleCommand( ); while (currentToken.kind==Token.SEMICOLON) { acceptIt( );// because SEMICOLON has just been checked parseSingleCommand( ); }
16
Syntax Analysis (Chapter 4) 16 Example: Generation of parseSingleDeclaration single-Declaration ::= const Identifier ~ Expression | var Identifier : Type-denoter single-Declaration ::= const Identifier ~ Expression | var Identifier : Type-denoter private void parseSingleDeclaration( ) { parse const Identifier ~ Expression | var Identifier : Type-denoter } private void parseSingleDeclaration( ) { parse const Identifier ~ Expression | var Identifier : Type-denoter } private void parseSingleDeclaration( ) { switch (currentToken.kind) { case Token.CONST: parse const Identifier ~ Expression case Token.VAR: parse var Identifier : Type-denoter default: report syntax error } private void parseSingleDeclaration( ) { switch (currentToken.kind) { case Token.CONST: parse const Identifier ~ Expression case Token.VAR: parse var Identifier : Type-denoter default: report syntax error } private void parseSingleDeclaration( ) { switch (currentToken.kind) { case Token.CONST: parse const parse Identifier parse ~ parse Expression case Token.VAR: parse var Identifier : Type-denoter default: report syntax error } private void parseSingleDeclaration( ) { switch (currentToken.kind) { case Token.CONST: parse const parse Identifier parse ~ parse Expression case Token.VAR: parse var Identifier : Type-denoter default: report syntax error } private void parseSingleDeclaration( ) { switch (currentToken.kind) { case Token.CONST: acceptIt( ); parseIdentifier( ); accept(Token.IS); parseExpression( ); case Token.VAR: parse var Identifier : Type-denoter default: report syntax error } private void parseSingleDeclaration( ) { switch (currentToken.kind) { case Token.CONST: acceptIt( ); parseIdentifier( ); accept(Token.IS); parseExpression( ); case Token.VAR: parse var Identifier : Type-denoter default: report syntax error } private void parseSingleDeclaration( ) { switch (currentToken.kind) { case Token.CONST: acceptIt( ); parseIdentifier( ); accept(Token.IS); parseExpression( ); case Token.VAR: acceptIt( ); parseIdentifier( ); accept(Token.COLON); parseTypeDenoter( ); default: report syntax error } private void parseSingleDeclaration( ) { switch (currentToken.kind) { case Token.CONST: acceptIt( ); parseIdentifier( ); accept(Token.IS); parseExpression( ); case Token.VAR: acceptIt( ); parseIdentifier( ); accept(Token.COLON); parseTypeDenoter( ); default: report syntax error }
17
Syntax Analysis (Chapter 4) 17 LL 1 Grammars The presented algorithm to convert EBNF into a parser does not work for all possible grammars. It only works for so called “LL 1” grammars. Basically, an LL 1 grammar is a grammar which can be parsed with a top-down parser with a lookahead (in the input stream of tokens) of one token. What grammars are LL 1? How can we recognize that a grammar is (or is not) LL 1? => We can deduce the necessary conditions from the parser generation algorithm.
18
Syntax Analysis (Chapter 4) 18 LL 1 Grammars parse X* while (currentToken.kind is in starters[X]) { parse X } while (currentToken.kind is in starters[X]) { parse X } parse X |Y switch (currentToken.kind) { cases in starters[X]: parse X break; cases in starters[Y]: parse Y break; default: if neither X nor Y generates then report syntax error } switch (currentToken.kind) { cases in starters[X]: parse X break; cases in starters[Y]: parse Y break; default: if neither X nor Y generates then report syntax error } Conditions: starters[X] and starters[Y] must be disjoint sets, and if either X or Y generates then must also be disjoint from the set of tokens that can immediately follow X | Y Condition: starters[X] must be disjoint from the set of tokens that can immediately follow X *
19
Syntax Analysis (Chapter 4) 19 LL 1 grammars and left factorization single-Command ::= V-name := Expression | Identifier ( Expression ) |... V-name ::= Identifier single-Command ::= V-name := Expression | Identifier ( Expression ) |... V-name ::= Identifier The original Mini-Triangle grammar is not LL 1: For example: Starters[ V-name := Expression ] = Starters[ V-name ] = Starters[ Identifier ] Starters[ Identifier ( Expression ) ] = Starters[ Identifier ] NOT DISJOINT!
20
Syntax Analysis (Chapter 4) 20 LL 1 grammars: left factorization private void parseSingleCommand( ) { switch (currentToken.kind) { case Token.IDENTIFIER: parse V-name := Expression case Token.IDENTIFIER: parse Identifier ( Expression )... other cases... default: report syntax error } private void parseSingleCommand( ) { switch (currentToken.kind) { case Token.IDENTIFIER: parse V-name := Expression case Token.IDENTIFIER: parse Identifier ( Expression )... other cases... default: report syntax error } single-Command ::= V-name := Expression | Identifier ( Expression ) |... single-Command ::= V-name := Expression | Identifier ( Expression ) |... What happens when we generate a RD parser from a non LL 1 grammar? wrong: overlapping cases
21
Syntax Analysis (Chapter 4) 21 LL 1 grammars: left factorization single-Command ::= V-name := Expression | Identifier ( Expression ) |... single-Command ::= V-name := Expression | Identifier ( Expression ) |... Left factorization (and substitution of V-name) single-Command ::= Identifier ( := Expression | ( Expression ) ) |... single-Command ::= Identifier ( := Expression | ( Expression ) ) |...
22
Syntax Analysis (Chapter 4) 22 LL 1 Grammars: left recursion elimination Command ::= single-Command | Command ; single-Command Command ::= single-Command | Command ; single-Command public void parseCommand( ) { switch (currentToken.kind) { case in starters[single-Command] parseSingleCommand( ); case in starters[Command] parseCommand( ); accept(Token.SEMICOLON); parseSingleCommand( ); default: report syntax error } public void parseCommand( ) { switch (currentToken.kind) { case in starters[single-Command] parseSingleCommand( ); case in starters[Command] parseCommand( ); accept(Token.SEMICOLON); parseSingleCommand( ); default: report syntax error } What happens if we don’t perform left-recursion elimination? wrong: overlapping cases
23
Syntax Analysis (Chapter 4) 23 LL 1 Grammars: left recursion elimination Command ::= single-Command | Command ; single-Command Command ::= single-Command | Command ; single-Command Left recursion elimination Command ::= single-Command (; single-Command)* Command ::= single-Command (; single-Command)*
24
Syntax Analysis (Chapter 4) 24 Abstract Syntax Trees So far we have talked about how to build a recursive descent parser which recognizes a given language described by an (LL 1) EBNF grammar. Next we will look at –how to represent AST as data structures. –how to modify the parser to construct an AST data structure. We make heavy use of Object–Oriented Programming! (classes, inheritance, dynamic method binding)
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.