Build an interpreter and a compiler to C ++ for the language BOOLexp using flex and bison
C O
N F ID
E N
T IA
L D
R A
F T
Chapter 10
Automatic Program Generation
Author: Saverio Perugini Copyright © 2018 by Saverio Perugini ALL RIGHTS RESERVED
10.1 Chapter Objectives
• Establish an understanding of flex and bison.
• Differientiate between . . . .
• Introduce . . . .
10.2 Scanner Generation: flex
10.2.1 Outline
10.2.2 Linux Tools for Automatically Generating Scanners and Parsers
flex and bison are the GNU versions of lex and yacc (yet another com- piler compiler), respectively.
10.2.3 Structure of a flex Specification:
277
C O
N F ID
E N
T IA
L D
R A
F T278 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
Listing 10.2: Our first flex program: cat (version 0).
1
2 %% 3
4 %% 5
6 /* c a l l e d by f l e x when EOF r e a c h e d */ 7 i n t yywrap ( void ) { 8 /* c o n v e n t i o n i s t o r e t u r n 1 */ 9 r e t u r n 1 ;
10 } 11
12 i n t main ( void ) { 13 /* main e n t r y p o i n t f o r f l e x */ 14 yylex ( ) ; 15 r e t u r n 0 ; 16 }
Listing 10.1: Structure of a flex specification.
1 /* d e f i n i t i o n s */ 2
3 %% 4
5 /* a s e t o f p a t t e r n−a c t i o n r u l e s */ 6
7 %% 8
9 /* s u b r o u t i n e s */
10.2.4 Our First flex Program: cat (version 0)
10.2.5 noop
10.2.6 cat (version 1)
10.2.7 Running flex to Automatically Generate a Scanner
1 $ flex c a t . l # p r o d u ce s l e x . yy . c 2 $ gcc lex . yy . c # p r o d u ce s a . out , t h e e x e c u t a b l e f o r t h e s c a n n e r 3 $ . /a . out # r u n s t h e s c a n n e r
C O
N F ID
E N
T IA
L D
R A
F T10.2. SCANNER GENERATION: FLEX 279
Listing 10.3: Noop: noop.l.
1 /* noop . l */ 2
3 %% 4
5 . { } 6 \n { } 7
8 %% 9
10 i n t yywrap ( ) { 11 r e t u r n 1 ; 12 } 13
14 i n t main ( ) { 15 yylex ( ) ; 16 r e t u r n 0 ; 17 }
Listing 10.4: cat version 1.
1 /* c a t 1 . l */ 2
3 %% 4
5 . /* match any c h a r a c t e r e x c e p t n e w l i n e */ printf ( "%s" , yytext) ; 6
7 \n /* match n e w l i n e */ printf ( "\n") ; 8
9 %% 10
11 i n t yywrap ( void ) { 12 r e t u r n 1 ; 13 } 14
15 i n t main ( void ) { 16 yylex ( ) ; 17 r e t u r n 0 ; 18 }
C O
N F ID
E N
T IA
L D
R A
F T280 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
Listing 10.5: cat version 2.
1 /* c a t 2 . l */ 2
3 %% 4
5 . ECHO ; 6
7 \n ECHO ; 8
9 %% 10
11 i n t yywrap ( void ) { 12 r e t u r n 1 ; 13 } 14
15 i n t main ( i n t argc , c h a r * * argv) { 16 printf ( ":%s:\n" , argv [ 1 ] ) ; 17 i f ( ( yyin = fopen ( argv [ 1 ] , "r" ) ) == NULL) 18 printf ( "broken\n") ; 19 i f ( yyin == stdin) 20 printf ( "here\n" ) ; 21 e l s e
22 printf ( "there\n" ) ; 23
24 yylex ( ) ; 25 fclose ( yyin) ; 26 r e t u r n 0 ; 27 }
. . . or use a Makefile (more on this later) 10.2.8 cat (version 2)
10.2.9 cat (version 3)
10.2.10 cat -n (version 4)
10.2.11 cat -n (version 5)
10.2.12 Word Count
10.2.13 Pattern Overlap
10.2.14 Identifying Identifiers
10.2.15 Matching Quoted Strings
10.2.16 States
• %s ONE creates the (regular) start state ONE
C O
N F ID
E N
T IA
L D
R A
F T10.2. SCANNER GENERATION: FLEX 281
Listing 10.6: cat version 3.
1 /* c a t 3 . l */ 2
3 %{ 4 i n t cc= 0 ; 5 %} 6
7 %% 8
9 . { cc+ + ; ECHO ; } 10
11 \n { cc+ + ; ECHO ; } 12
13 %% 14
15 i n t yywrap ( void ) { 16 r e t u r n 1 ; 17 } 18
19 i n t main ( i n t argc , c h a r * * argv) { 20 yyin = fopen ( argv [ 1 ] , "r" ) ; 21 yylex ( ) ; 22 fclose ( yyin) ; 23 printf ( "%d characters\n" , cc ) ; 24 r e t u r n 0 ; 25 }
C O
N F ID
E N
T IA
L D
R A
F T282 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
Listing 10.7: cat -n version 4.
1 /* c a t 4 . l ( c a t −n ) */ 2
3 %{ 4 i n t cc = 0 ; 5 i n t lineno = 0 ; 6 %} 7
8 %% 9
10 ^ . * \n { cc += strlen( yytext) ; 11 printf ( "%d %s" , ++lineno , yytext) ; } 12 %% 13
14 i n t yywrap ( ) { 15 r e t u r n 1 ; 16 } 17
18 i n t main( i n t argc , c h a r * * argv) { 19 yyin = fopen ( argv [ 1 ] , "r" ) ; 20 yylex ( ) ; 21 printf ( "%d characters.\n" , cc) ; 22 fclose( yyin) ; 23 r e t u r n 0 ; 24 }
C O
N F ID
E N
T IA
L D
R A
F T10.2. SCANNER GENERATION: FLEX 283
Listing 10.8: cat -n version 5.
1 /* c a t 5 . l ( c a t −n ) */ 2
3 %option yylineno 4
5 %{ 6 i n t cc = 0 ; 7 %} 8
9 %% 10 ^ . * \n { cc += strlen( yytext) ; 11 printf( "%4d\t%s" , yylineno−1 , yytext) ; } 12
13 %% 14
15 i n t yywrap ( void ) { 16 r e t u r n 1 ; 17 } 18
19 i n t main ( i n t argc , c h a r * * argv) { 20 yyin = fopen ( argv [ 1 ] , "r" ) ; 21 yylex ( ) ; 22 printf ( "%d characters.\n" , cc) ; 23 fclose ( yyin) ; 24 r e t u r n 0 ; 25 }
C O
N F ID
E N
T IA
L D
R A
F T284 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
Listing 10.9: Word count (wc).
1 %{ 2 i n t cc = 0 ; 3 i n t wc = 0 ; 4 i n t lc = 0 ; 5 %} 6
7
8 %% 9
10 \n { lc+ + ; cc+ + ; } 11
12 [ \t] { cc+ + ; } 13
14 [ ^ \t\n] + { wc+ + ; cc += yyleng ; /* c o u n t a n y t h i n g bu t w h i t e s p a c e */ } 15
16 %% 17
18 i n t yywrap ( ) { 19 r e t u r n 1 ; 20 } 21
22 i n t main( i n t argc , c h a r * * argv) { 23 yyin = fopen ( argv [ 1 ] , "r" ) ; 24 yylex ( ) ; 25 printf ( "%8d%8d%8d\n" , lc , wc , cc) ; 26 fclose( yyin) ; 27 r e t u r n 0 ; 28 }
C O
N F ID
E N
T IA
L D
R A
F T10.2. SCANNER GENERATION: FLEX 285
Listing 10.10: Pattern overlap in word count (wc2.l).
1 %{ 2 i n t cc = 0 ; 3 i n t wc = 0 ; 4 i n t lc = 0 ; 5 %} 6
7
8 %% 9
10 [ ] { printf( "Found a space.\n") ; } 11
12 [ \t] { cc+ + ; } 13
14 \n { lc+ + ; cc+ + ; } 15
16 [ ^ \t\n] + { wc+ + ; cc += yyleng ; /* c o u n t a n y t h i n g bu t w h i t e s p a c e */ } 17
18 %% 19
20 i n t yywrap ( ) { 21 r e t u r n 1 ; 22 } 23
24 i n t main( i n t argc , c h a r * * argv) { 25 yyin = fopen ( argv [ 1 ] , "r" ) ; 26 yylex ( ) ; 27 printf ( "%8d%8d%8d\n" , lc , wc , cc) ; 28 fclose( yyin) ; 29 r e t u r n 0 ; 30 }
C O
N F ID
E N
T IA
L D
R A
F T286 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
Listing 10.11: Identifying identifiers (idcount.l).
1 %{ 2 i n t idcount= 0 ; 3 %} 4
5 alpha [ _a−zA−Z ] 6 alphanumeric [ _a−zA−Z0−9] 7 digit [0−9] 8
9
10 %% 11
12 { alpha } { alphanumeric} * { idcount+ + ; printf( "%s\n" , yytext) ; } 13 { alpha } ( { alpha} | { digit } ) * { idcount+ + ; ECHO ; printf( "\n" ) ; } 14
15 . { } 16 \n { } 17
18 %% 19
20 i n t yywrap ( void ) { 21 r e t u r n 1 ; 22 } 23
24 i n t main ( i n t argc , c h a r * * argv) { 25 yyin = fopen ( argv [ 1 ] , "r" ) ; 26 yylex ( ) ; 27 fclose( yyin) ; 28 printf ( "This program contains %d identifiers.\n" , idcount) ; 29 r e t u r n 0 ; 30 }
C O
N F ID
E N
T IA
L D
R A
F T10.2. SCANNER GENERATION: FLEX 287
Listing 10.12: Matching quoted strings (quotedStrings.l).
1 %{ 2 # i n c l u d e < s t r i n g . h> 3 e x t e r n i n t yy_flex_debug; 4 c h a r * yylval = NULL ; 5 %} 6
7 %% 8
9 [ "][^"\n ] * [ "] { printf (":%s: \n", yytext); 10 yylval = strdup(yytext+1);
11 /* yylval[strlen(yylval)-1] = '\0'; */
12 yylval[yyleng-2] = '\0';
13 printf (":%s: \n", yylval); } 14
15 ["] [ ^ "\n]*[\n] { fprintf (stderr, ":%s: \n", yytext); 16 warning("Invalid string : "); 17 printf (":%s: \n", yytext+1); } 18
19 \n { }
20 . { }
21
22 %%
23
24 int yywrap() {
25 return 1;
26 }
27
28 int warning (char* s) {
29 fprintf (stderr, "%s\n", s); 30 return 2;
31 }
32
33 int main(int argc, char** argv) {
34 /* flex -d to enable debugging statements */
35 yy_flex_debug = 1;
36 yylex();
37 return 0;
38 }
C O
N F ID
E N
T IA
L D
R A
F T288 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
all
a.out
lex.yy.c
Cstrings.l
Figure 10.1: Makefile dependency graph for C strings.
Table 10.1: Pattern matching primitives. Metacharacter Matches . any character except newline \n newline
* zero or more copies of the preceding expression + one or more copies of the preceding expression ? zero or one copy of the preceding expression ˆ beginning of line $ end of line a | b a or b (ab)+ one or more copies of ab (grouping) “a+b” literal “a+b” (C escapes still work) [] character class
• ‘rules that do not have start states can apply in any state’ [Nie, p. 172]
• %x TWO creates the exclusive start state TWO
• ‘a rule with no start state is not matched when an exclusive state is active’ [Nie, p. 172]
10.2.17 Matching C Strings
10.2.18 Conceptual Exercises for Section 10.2
Exercise 10.2.1: Define a regular expression to match a string containing balanced parentheses (e.g., ((())()) is balanced, (()() is unbalanced) not state why it is not possible.
C O
N F ID
E N
T IA
L D
R A
F T10.2. SCANNER GENERATION: FLEX 289
Listing 10.13: States (states.l).
1 %{ 2 %} 3
4 %x ONE 5 %x TWO 6
7
8 %% 9
10 a { BEGIN ONE ; printf( "in ZERO; read a; goto ONE\n") ; } 11
12 b { BEGIN TWO ; printf( "in ZERO; read b; goto TWO\n") ; } 13
14 <TWO>a { printf ( "in TWO; read a; goto 0\n") ; BEGIN 0 ; } 15 <TWO>b { printf ( "in TWO; read b; goto 0\n") ; BEGIN 0 ; } 16 <ONE>a { printf ( "in ONE; read a; goto TWO\n" ) ; BEGIN TWO ; } 17 <ONE>b { printf ( "in ONE; read b; goto TWO\n" ) ; BEGIN TWO ; } 18
19 . { } 20 \n { } 21 <ONE> . { } 22 <ONE>\n { } 23 <TWO> . { } 24 <TWO>\n { } 25
26 %% 27
28 i n t yywrap ( ) { 29 r e t u r n 1 ; 30 } 31
32 i n t main ( ) { 33 yylex ( ) ; 34 r e t u r n 0 ; 35 }
C O
N F ID
E N
T IA
L D
R A
F T290 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
Listing 10.14: Matching C strings (Cstrings.l).
1 %{ 2 e x t e r n i n t yy_flex_debug; 3 c h a r buf [ 1 0 0 ] ; 4 c h a r * s = NULL ; 5
6 %} 7
8 %x INQUOTE 9
10 %% 11
12 \" { BEGIN INQUOTE; s = buf; } 13
14 <INQUOTE>\\\" { *s++ = '\"'; fprintf(stderr, "found escaped quote\n"←֓ ); }
15 <INQUOTE>\\\n { fprintf(stderr, "found escaped newline\n"); } 16 <INQUOTE>\\n { *s++ = '\n'; fprintf(stderr, "found newline\n"); } 17 <INQUOTE>\\t { *s++ = '\t'; fprintf(stderr, "found tab\n"); } 18
19 <INQUOTE>[" ] { *s = '\0' ; 20 BEGIN 0 ; 21 printf ( "\nFound :%s:\n" , buf) ; } 22
23 <INQUOTE>\n { BEGIN 0 ; fprintf ( stderr , "Invalid string.\n" ) ; /* ←֓ e x i t ( 1 ) ; */ }
24
25
26 <INQUOTE> . { *s++ = *yytext ; } 27
28 \n { } 29 . { } 30
31 %% 32
33 i n t yywrap ( ) { 34 r e t u r n 1 ; 35 } 36
37 i n t main ( ) { 38 yy_flex_debug = 0 ; 39 yylex ( ) ; 40 r e t u r n 0 ; 41 }
C O
N F ID
E N
T IA
L D
R A
F T10.2. SCANNER GENERATION: FLEX 291
Listing 10.15: Makefile for C strings (Makefile).
1 SRC = Cstrings . l 2 CC = gcc 3 LEX = flex 4 LEX_FLAGS = −d 5 OBJ = lexer 6
7 all : $( OBJ) 8
9 $ ( OBJ) : lex . yy . c 10 $ ( CC) −o $ ( OBJ) lex . yy . c 11
12 lex . yy . c : $ ( SRC) 13 $ ( LEX) $ ( LEX_FLAGS) $ ( SRC) 14
15 clean : 16 @−rm lex . yy . c $( OBJ)
Table 10.2: Pattern matching examples. Expression Matches
abc abc abc* ab, abc, abcc, abccc, . . . abc+ abc, abcc, abccc, abcccc, . . . a(bc)+ abc, abcbc, abcbcbc, ... a(bc)? a, abc [abc] one of: a, b, c [a-z] any letter, a through z [a\-z] one of: a, -, z [-az] one of: -, a, z [A-Za-z0-9]+ one or more alphanumeric characters [ \t\n]+ whitespace [ˆab] anything except: a, b [aˆb] a, ˆ, b [a | b] a, |, b a | b a, b
C O
N F ID
E N
T IA
L D
R A
F T292 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
Table 10.3: flex predefined variables. Name Function
int yylex(void) call to invoke lexer, returns token char* yytext pointer to matched string yyleng length of matched string yylval value associated with token int yywrap(void) wrapup, return 1 if done, 0 if not done FILE* yyout output file FILE* yyin input file INITIAL initial start condition BEGIN condition switch start condition ECHO write matched string
10.2.19 Programming Exercises for Section 10.2
Exercise 10.2.2: Define a flex specification for a program that writes to stdout each line of its standard input with all leading and trailing whites- pace purged from every line.
Exercise 10.2.3: Define a flex specification for a program that writes to stdout each line of its standard input with all leading and trailing whites- pace purged from every line, and all blank lines purged.
Exercise 10.2.4: Define a flex specification for the Linux wc command. You need not handle file I/O or command-line options (assume -l, -w, and -c are always present).
Exercise 10.2.5: Define a flex specification for the Linux wc command. The scanner generated must support both standard input and file input. You need not handle command-line options (assume -l, -w, and -c are always present).
Exercise 10.2.6: Consider the input stream given in Exercise 8.3.. Define a flex specification for a program to convert each line of standard input in the form (<last>,<first>) to (<first> 2 <first>) and print the results to stdout, where 2 represents a single space character.
Exercise 10.2.7: Consider the input stream given in Programming Ex- ercise 8.3.. Define a flex specification for a program to con- vert each line of standard input in the form (<last>,<first>) to
C O
N F ID
E N
T IA
L D
R A
F T10.2. SCANNER GENERATION: FLEX 293
(<first> 2 <first>) and print the results, with any leading and trail- ing whitespace, and all blank lines, purged, to standard output, where 2 represents a single space character.
Exercise 10.2.8: Rewrite the flex specification for matching quoted strings in Listing 10.12 by combining the two pattern-action rules into one pattern-action rule.
10.2.20 Programming Projects for Section 10.2
Exercise 10.2.1: Automatically generate a lexical analyzer which outputs the uncommented and commented included header filenames from a stream of C/C++ source code.
Requirements:
a) Your program must read from standard input and file input, but always write to standard output.
b) Your program must support only two command-line options (-u and -c) and combinations of them (e.g., -uc and -cu).
c) When run with no command-line options, your program must print both uncommented and commented included header filenames (and nothing else) using the format used in the sample output given below.
d) When run with the -u command-line option, your program must print only the uncommented included header filenames (and nothing else) us- ing the format used in the sample output given below.
e) When run with the -c command-line option, your program must print only the commented included header filenames (and nothing else) using the format used in the sample output given below.
f) When run with the -u and -c command-line options or the -uc or -cu command-line options, your program must print both the uncommented and commented included header filenames (and nothing else) using the format used in the sample output given below.
g) If an invalid option <option> is given, the program must print ./showheaders: Illegal option: <option> and a usage mes- sage to stderr and halt with a exit status 1 as shown below.
C O
N F ID
E N
T IA
L D
R A
F T294 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
1 $ . /showheaders −t 2 . /showheaders : Illegal option −t 3 Usage : showheaders [−cu] [ file( s ) . . . ] 4 $ echo $? 5 1
h) If an invalid file <file> is given, the program must print ./showheaders: Invalid file: <file> and a usage message to stderr and and halt with exit status 2.
1 $ . /showheaders somefile 2 . /showheaders : Invalid file : somefile 3 Usage : showheaders [−cu] [ file( s ) . . . ] 4 $ echo $? 5 2
i) You may assume that the input stream will never contain more than fifty (uncommented or commented) included header filenames.
j) You may assume that if you encounter an opening /* comment token that the closing */ comment token will always be on the same line. More generally, you may assume that the input stream will be syntactically valid C/C++ source code.
k) Note that comments may contain both text and an include file:
1 /* T h i s i s my comment abo u t # i n c l u d e < s t d i o . h> */ 2
3 // T h i s i s my comment abo u t # i n c l u d e < s t d i o . h>
l) Your solution must contain only a flex specification file and a Makefile (i.e., no other source files).
m) Use macros and substitutions (e.g., digit [0-9]), where possible and appropriate, to simply the pattern-matching rules in your flex specifi- cation file.
n) Develop a Makefile which builds your lexical analyzer. Your Makefile must include target directives for every derived file produced during the compilation process (i.e., each program, each object file, and any other intermediate files produced during code generation and com- pilation). Make sure that each directive also lists all files on which the derived file depends in its dependency list. Also, your Makefile must
C O
N F ID
E N
T IA
L D
R A
F T10.3. PARSER GENERATION: BISON 295
yes / nosource program (regular grammar) list of
tokens
(context−free grammar) parserscanner(string or
list of lexemes)
Figure 10.2: Simplified view of scanning and parsing: the front end.
be written so carries out only the commands necessary to bring any pro- duced file up-to-date. Your Makefile must do just enough, but no ex- tra, work to bring showheaders (the final executable for your lexical analyzer) up-to-date every time make is invoked. In addition, it must have an all directive and a clean directive to remove all generated files. Use variables where appropriate in your Makefile to improve its readability. Your Makefile must bring everything up-to-date, us- ing only lex and gcc, without any warnings or errors, when make is invoked.
Sample test data is available at http://perugini.cps.udayton.edu/ teaching/books/SPUC/www/files/showheadersdata.tar, and a sample test session with showheaders on that data is available at http://perugini.cps.udayton.edu/teaching/books/SPUC/ www/files/showheaderstestsession.txt.
Exercise 10.2.2: Complete Programming Project 10.2.1 in Go, subject to all the requirements given in that specification. Use the Nex (nex) lexical analyzer generator for Go available at: https://crypto.stanford. edu/~blynn/nex/.
10.3 Parser Generation: bison
10.3.1 Scanning and Parsing
10.3.2 Evaluating Arithmetic Expressions in Linux
1 $ expr 2 + 3 2 5 3 $ expr 2 + 3 \* 4 4 14 5 $ expr 2 \* 3 + 4 6 10
C O
N F ID
E N
T IA
L D
R A
F T296 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
yes / no
yacclex
source program list of
tokens(string or list of lexemes)
.yregular grammar ( ).l context−free grammar ( )
lex.yy.c
scanner parser .tab.h .tab.c
Figure 10.3: Simplified view of scanning and parsing: the front end with flex and bison.
yes / no
id1 = id2 * id3 + id4
scanner
parser
list of tokens
"n = x * y + z"source program (string)
Figure 10.4: More detailed view of scanning and parsing.
7 $ expr "2 + 3 * 4"
8 2 + 3 * 4
1 $ bc −l 2 bc 1 . 0 6 3 Copyright 1991−1994 , 1 9 9 7 , 1 9 9 8 , 2000 Free Software Foundation , Inc . 4 This is free software with ABSOLUTELY NO WARRANTY . 5 For details type `warranty'.
6 23+47
7 70
8 2 + 3
9 5
10 2 + 3 * 4
11 14
12 2 * 3 + 4
13 10
C O
N F ID
E N
T IA
L D
R A
F T10.3. PARSER GENERATION: BISON 297
.tab.c
lex regular grammar ( )
yacc context−free grammar ( )
id1 = id2 * id3 + id4
scanner
list of tokens
"n = x * y + z"source program (string)
yes / no =
.l
.y
lex.yy.c
parser .tab.h
Figure 10.5: More detailed view of scanning and parsing with flex and bison.
14 2 ^ 3
15 8
16 ^D
10.3.3 Calculator (version 1)
The following is a context-free grammar in ENBF defining a language of calculator expressions which we use as a running example in this chapter.
<program> ::= <program> <expr> \n | <expr> \n <expr> ::= (<list>) | a
<list> ::= <expr> | <expr> <list> <expr> ::= <integer> <expr> ::= − <expr>
<expr> ::= <expr> + <expr> <expr> ::= <expr> * <expr>
<integer> ::= 1 | 2 | 3 | . . . |∞
Hack to deal with an ambiguous grammar. bison Conflicts
%left ’+’ ’-’
%left ’*’ ’/’
1 %token INTEGER
C O
N F ID
E N
T IA
L D
R A
F T298 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
Listing 10.16: calc1.l.
1 %{ 2 # i n c l u d e "calc1.tab.h"
3 /* 4 # d e f i n e YYSTYPE i n t 5 e x t e r n YYSTYPE y y l v a l ; 6 */ 7 %} 8
9 %% 10
11 0 { 12 /* g e t i n t e g e r v a l u e o f INTEGER t o k e n */ 13 yylval = atoi ( yytext) ; 14 r e t u r n INTEGER ; 15 } 16
17 [ 1−9 ] [ 0−9 ] * { 18 /* g e t i n t e g e r v a l u e o f INTEGER t o k e n */ 19 yylval = atoi ( yytext) ; 20 r e t u r n INTEGER ; 21 } 22
23 [−+\n ] { r e t u r n *yytext ; } 24
25 [ \t] ; /* s k i p w h i t e s p a c e */ 26
27 . yyerror ( "invalid character" ) ; 28
29 %% 30
31 i n t yywrap ( void ) { 32 r e t u r n 1 ; 33 }
C O
N F ID
E N
T IA
L D
R A
F T10.3. PARSER GENERATION: BISON 299
Listing 10.17: calc1.y.
1 /* t o k e n v a l u e s t y p i c a l l y s t a r t around 258 2 b e c a u s e v a l u e s 0−255 a r e r e s e r v e d f o r c h a r a c t e r v a l u e s and 3 l e x r e s e r v e s s e v e r a l v a l u e s f o r end−o f−f i l e and e r r o r p r o c e s s i n g 4 */ 5
6 /* p r o d u ce s " # d e f i n e INTEGER 2 5 8 " i n y . t a b . c on our s y s t e m */ 7 %token INTEGER 8
9 %{ 10 # i n c l u d e < s t d i o . h> 11 # d e f i n e YYDEBUG 0 12 %} 13
14 %left '+' '-' 15
16 %% 17
18 program : program expr '\n' { printf ( "%d\n" , $2) ; } 19 | expr '\n' { printf ( "%d\n" , $1 ) ; } 20 ; 21
22 expr : INTEGER { $$ = $1 ; /* d e f a u l t a c t i o n : pop , push */ } 23
24 | expr '+' expr { 25 /* a d d i t i o n */ 26 $$ = $1 + $3 ; 27 } 28
29 | expr '-' expr { 30 /* s u b t r a c t i o n */ 31 $$ = $1 − $3 ; 32 } 33 ; 34
35 %% 36
37 i n t yyerror ( c h a r * s ) { 38 fprintf ( stderr , "%s\n" , s ) ; 39 r e t u r n 0 ; 40 } 41
42 i n t main ( void ) { 43 # i f YYDEBUG 44 yydebug = 0 ; 45 // y y _ f l e x _ d e b u g = 1 ; 46 # e n d i f
47 yyparse ( ) ; 48 r e t u r n 0 ; 49 }
C O
N F ID
E N
T IA
L D
R A
F T300 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
value stack
contains terminals
represents current parsing state
and non−terminals; an array of YYSTYPE elements
$$ = top of stack
<expr>
’+’
<expr>
31
’+’
23
$3
$2
$1
tokens yylvals
<expr> $$ 54
parse stack
Figure 10.6: Parse stack and value stacks in bison.
inptut (e.g., source code) gram.y
lex.yy.c
(contains
tokens.l
(contains
gcc
s#include
gram.tab.c
gram.tab.h
output (e.g., parse tree)
(defines )
of grammar) (EBNF specification
YYSTYPE
)yylex()
yyparse() )
(regular expression specification of
tokens)
bison
flex
a.out
Figure 10.7: Marriage of flex and bison.
2 /* p r o d u ce s " # d e f i n e INTEGER 2 5 8 " i n c a l c . t a b . c 3 b e c a u s e v a l u e s 0−255 a r e r e s e r v e d f o r c h a r a c t e r v a l u e s , and 4 l e x r e s e r v e s s e v e r a l v a l u e s f o r end−o f−f i l e and e r r o r p r o c e s s i n g 5 and , t h e r e f o r e , t o k e n v a l u e s t y p i c a l l y s t a r t around 258 */
10.3.4 Marriage of flex and bison
10.3.5 Running bison (in conjunction with flex) to Generate a Parser
[Nie][p. 5]
[Nie][p. 5] Fig. 10.7 illustrates how flex and bison collaborate to generate a
parser.
1 $ flex tokens . l # p r o d u ce s l e x . yy . c
C O
N F ID
E N
T IA
L D
R A
F T10.3. PARSER GENERATION: BISON 301
inptut (e.g., source code)
lex.yy.c
(contains
(contains
gcc
s#include
output (e.g., parse tree)
(defines )
of grammar) (EBNF specification
YYSTYPE
)yylex()
yyparse() )
(regular expression specification of
tokens)
bison
flex
a.out
calc1.y
calc1.l
calc1.tab.c
calc1.tab.h
Figure 10.8: Marriage of flex and bison in calculator.
2 $ bison −d gram . y # p r o d u ce s gram . t a b . c and gram . t a b . h 3 $ gcc −c gram . tab . c # p r o d u ce s gram . t a b . o 4 $ gcc −c lex . yy . c # p r o d u ce s l e x . yy . o 5 $ gcc −o parser gram . tab . o lex . yy . o # p r o d u ce s p a r s e r 6 $ . /parser < . . .
1 $ flex calc1 . l # p r o d u ce s l e x . yy . c 2 $ bison −d calc1 . y # p r o d u ce s c a l c 1 . t a b . c and c a l c 1 . t a b . h 3 $ gcc −c calc1 . tab . c # p r o d u ce s c a l c 1 . t a b . o 4 $ gcc −c lex . yy . c # p r o d u ce s l e x . yy . o 5 $ gcc −o parser calc1 . tab . o lex . yy . o # p r o d u ce s p a r s e r 6 $ . /calc1 < . . .
10.3.6 Calculator (version 2)
We extend the calculator of the previous section to incorporate the follow- ing new features:
• multiplication (*) and division (/) arithmetic operators,
• a unary minus operator (−),
• a exponentiation operator (ˆ) for non-negative exponents,
• parentheses to override operator precedence,
• single-character variables, and
• a print statement.
C O
N F ID
E N
T IA
L D
R A
F T302 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
Listing 10.18: Makefile for calculator (version 1).
1 SRC = calc1 2 CC = gcc 3 LEX = flex 4 #LEX_FLAGS = −d 5 LEX_FLAGS = 6 YACC = bison 7 YACC_FLAGS = −d −t 8
9 all : $( SRC) 10
11 $ ( SRC) : lex . yy . o $ ( SRC) . tab . o 12 $ ( CC) lex . yy . o $ ( SRC) . tab . o −o $ ( SRC) 13
14 lex . yy . o : lex . yy . c $ ( SRC) . tab . h 15 $ ( CC) −c lex . yy . c 16
17 lex . yy . c : $ ( SRC) . l 18 $ ( LEX) $ ( LEX_FLAGS) $ ( SRC) . l 19
20 $ ( SRC) . tab . o : $( SRC) . tab . c 21 $ ( CC) −c $ ( SRC) . tab . c 22
23 $ ( SRC) . tab . c : $( SRC) . y 24 $ ( YACC) $ ( YACC_FLAGS) $ ( SRC) . y 25
26 $ ( SRC) . tab . h : $( SRC) . y 27 $ ( YACC) $ ( YACC_FLAGS) $ ( SRC) . y 28
29 clean : 30 −rm * . [ cho] $( SRC)
C O
N F ID
E N
T IA
L D
R A
F T10.3. PARSER GENERATION: BISON 303
Listing 10.19: calc2.l.
1 %{ 2 # i n c l u d e "calc2.tab.h"
3 %} 4
5 %% 6
7 [ a−z] { /* t h e p o s i t i o n o f t h e c h a r a c t e r i n t h e a l p h a b e t 0 . . 2 5 */ 8 yylval = *yytext − 'a' ; 9 r e t u r n VARIABLE ; }
10
11 0 { yylval = atoi ( yytext) ; 12 r e t u r n INTEGER ; } 13
14 [ 1−9 ] [ 0−9 ] * { yylval = atoi ( yytext) ; 15 r e t u r n INTEGER ; } 16
17 [−+ ( ) = */ ^ ; \n ] { /* o p e r a t o r s */ r e t u r n *yytext ; } 18
19 print { /* o p e r a t o r */ r e t u r n PRINT ; } 20
21 [ \t] { /* s k i p w h i t e s p a c e */ } 22
23 . { /* a n y t h i n g e l s e i s an e r r o r */ yyerror ( "invalid character" ) ; } 24
25 %% 26
27 i n t yywrap ( void ) { 28 r e t u r n 1 ; 29 }
C O
N F ID
E N
T IA
L D
R A
F T304 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
Listing 10.20: calc2.y. 1 %token INTEGER VARIABLE PRINT 2 %right '=' 3 %left '+' '-' 4 %left '*' '/' 5 %right '^' 6 7 %{ 8 # i n c l u d e < s t d i o . h> 9 # i n c l u d e <math . h>
10 # d e f i n e SIZE 26 11 # d e f i n e YYDEBUG 0 12 i n t symtab[ SIZE ] ; 13 %} 14 15 %% 16 17 program : program statement ';' '\n' 18 | statement ';' '\n' 19 ; 20 21 statement : 22 expr 23 | PRINT expr { printf ( "%d\n" , $2 ) ; } 24 | VARIABLE '=' expr { symtab[ $1 ] = $3 ; } 25 ; 26 27 expr : 28 INTEGER 29 | VARIABLE { $$ = symtab[ $1 ] ; } 30 | '-' expr %prec '^' { $$ = $2* −1; } 31 | expr '*' expr { $$ = $1 * $3 ; } 32 | expr '/' expr { $$ = $1 / $3 ; } 33 | expr '+' expr { $$ = $1 + $3 ; } 34 | expr '-' expr { $$ = $1 − $3 ; } 35 | expr '^' expr { $$ = pow ( $1 , $3) ; } 36 | '(' expr ')' { $$ = $2 ; } 37 ; 38 39 %% 40 41 i n t yyerror ( c h a r * s ) { 42 fprintf ( stderr , "%s\n" , s ) ; 43 r e t u r n 0 ; 44 } 45 46 i n t main ( v o id ) { 47 # i f YYDEBUG 48 yydebug = 1 ; 49 # e n d i f 50 i n t i ; 51 f o r ( i= 0 ; i < SIZE ; i++) 52 symtab[ i ] = 0 ; 53 yyparse ( ) ; 54 r e t u r n 0 ; 55 }
C O
N F ID
E N
T IA
L D
R A
F T10.3. PARSER GENERATION: BISON 305
The following is sample input and output for the extended calculator (> is simply the prompt for input and will be the empty string in your system).
> 2 * (5 - 6);
> print 2 * (5 -6);
-2
> x = 6 / (7- 4);
> x;
> print x ;
2
> y= 3;
> y + -3 * x;
> print y + - 3 * x;
-3
> print y ^ x;
9
The syntactic aspects of these enchancements are expressed in the follow- ing context-free grammar in EBNF for calculator sentences:
<program> ::= <program> <stmt> ; \n | <stmt> ; \n
<stmt> ::= <expr> | print <expr> <stmt> ::= <variable> = <expr>
<expr> ::= <integer> | <var> <expr> ::= − <expr> <expr> ::= <expr> + <expr>
<expr> ::= <expr> − <expr> <expr> ::= <expr> * <expr>
<expr> ::= <expr> / <expr> <expr> ::= <expr> ˆ <expr>
<expr> ::= (<expr>) <integer> ::= 1 | 2 | 3 | . . . |∞ <variable> ::= a | b | c | . . . | z
The unary minus operator (−) has precedence over all other operators. The exponentiation operator (ˆ) is right-associative and has the second highest precedence. Identifiers for single-character variables are limited to the 26 lowercase alphabetic characters.
1 /* y i e l d s an i n t e g e r i n t h e r an g e 0−25 */ 2 /* a s c i i code f o r c h a r a c t e r ' a ' i s 97 */
C O
N F ID
E N
T IA
L D
R A
F T306 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
program output scanner
(regular grammar) grammar) (context−free
tokens parser
source program (string or
list of lexemes)
list of
Front End
interpreter
interpreting while parsing
Figure 10.9: Interpreting while parsing.
calc2.tab.h
tokens
source program
(string or list of lexemes)
list of
Front End
interpreter
interpreting while parsing
program output
(regular grammar) calc2.l scanner lex.yy.c
(context−free grammar) calc2.y
parser calc2.tab.c
Figure 10.10: Interpreting while parsing in calculator (version 1 and 2).
3 /* a s c i i code f o r c h a r a c t e r ' t ' i s 116 */ 4 yylval = *yytext − 'a' ;
The lexical analyzer must now return VARIABLE tokens in addition to INTEGER tokens.
The same Makefile from version 1 can be used for version 2 of the calculator.
(regular grammar) Interpreter
scanner list of
tokens parser grammar)
(context−freesource program (string or
parse tree
Front End
list of lexemes)
program input
program output
Figure 10.11: Interpretation.
C O
N F ID
E N
T IA
L D
R A
F T10.4. PUTTING IT ALL TOGETHER: TOWARDS INTERPRETERS 307
Interpreterscanner source program
(string or list of lexemes)
list of
tokens parser grammar)
(context−free parse tree
Front End
(regular grammar) program output
program input
interpreter (compiled to machine code)
(input to the interpreter)
(input to the interpreter)
(e.g., processor)
Figure 10.12: Alternate view of execution by interpretation.
translated program
scanner (regular grammar) list of
tokens parser grammar)source program
(string or parse tree
Front End
list of lexemes)
(context−free
Compiler
Interpreter (e.g., processor)
code generator/ translatoranalyzer
semantic
program output
program input
(e.g., object code)
Figure 10.13: Compilation.
10.4 Putting It All Together: Towards Interpreters
In this section, we extend the language for calculator sentences and its parser. Specifically, we
1. incorporate more features into the calculator,
2. construct a syntax tree during parsing, and
3. traverse the tree to evaluate a calculator program and produce output.
10.4.1 Calculator (version 3)
The additional features are
• the <, <=, >, >=, ==, and ! = binary comparison operators,
• selection through if and if–else statements,
• repetition through a while statement, and
• statement blocks beginning and ending with { and }, respectively.
C O
N F ID
E N
T IA
L D
R A
F T308 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
program output
mul id3 add id4 store id1
001101010110110 000110101010111 111100011100101 010101010101010
id1 = id2 * id3 + id4
=
+
scanner
parser
preprocessor
n = x * y + z
id1
Front End
n = x * y + z
code generator
Compiler
/* mathematical expression */
*
id2 id3
id4 parse tree
assembly code
assembler
object code
source program commented
list of tokens
list of lexemes
processorprogram input
load id2
Figure 10.14: Low-level view of execution by compilation.
(regular grammar) Interpreter
scanner list of
tokens parser grammar)
(context−freesource program (string or
parse tree
Front End
list of lexemes)
program output
Figure 10.15: Calculator expression interpretion.
C O
N F ID
E N
T IA
L D
R A
F T10.4. PUTTING IT ALL TOGETHER: TOWARDS INTERPRETERS 309
program output
list of
tokens
source program (string or
parse tree
Front End
list of lexemes)
(regular grammar)
lex.yy.c
scanner (calc3.l)
calc3.tab.c
(context−free grammar)
(calc3.y)
parser calc3.tab.h
Interpreter
interpreter.c
Figure 10.16: Calculator expression interpretion.
in assembly code
code generator/ translator
Compiler
(context−free
scanner (regular grammar) list of
tokens parser grammar)source program
(string or parse tree
Front End
list of lexemes)
translated program
Figure 10.17: Calculator expression compilation.
calc3.tab.h
code generator/ translator
Compiler
list of
tokens
source program (string or
parse tree
Front End
list of lexemes)
translated program
in assembly code
compiler.c
(regular grammar) (calc3.l)
scanner lex.yy.c
(context−free grammar)
(calc3.y)
calc3.tab.c
parser
Figure 10.18: Calculator expression compilation.
Front End
mul id3 add id4 store id1
id1 = id2 * id3 + id4tokens
=
+
scanner
parser
n = x * y + zsource program
id1
code generator
Compiler
*
id2 id3
id4 parse tree
assembly code
lex
yacc
regular grammar ( ).l
context−free grammar ( ).y
(mathematical expression)
load id2
Figure 10.19: .
C O
N F ID
E N
T IA
L D
R A
F T310 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
Front End
mul id3 add id4 store id1
id1 = id2 * id3 + id4tokens
=
+
n = x * y + zsource program
id1
*
id2 id3
id4
parse tree
assembly code
compiler.c
Compiler
code generator
lex
yacc
calc3.l (regular grammar)
calc3.y (context−free grammar) parser
calc3.tab.h calc3.tab.c
scanner lex.yy.c
(mathematical expression)
load id2
Figure 10.20: .
With these new features, the language understood by the calculator begins to resemble an imperative programming language. These features, which have the same semantics as in C, are expressed in the following context- free grammar in EBNF for calculator programs:
C O
N F ID
E N
T IA
L D
R A
F T10.4. PUTTING IT ALL TOGETHER: TOWARDS INTERPRETERS 311
<program> ::= <code> <code> ::= <code> <stmt> | <stmt> <stmt> ::= ; | print <expr> ;
<stmt> ::= <var> = <expr> ; <stmt> ::= while ( <expr> ) <stmt>
<stmt> ::= if ( <expr> ) <stmt> [ else <stmt> ] <stmt> ::= { <stmt_list> }
<stmt_list> ::= <stmt> | <stmt_list> <stmt> <expr> ::= <integer> | <var> |− <expr>
<expr> ::= <expr> + <expr> | <expr> − <expr> <expr> ::= <expr> * <expr> | <expr> / <expr> <expr> ::= <expr> < <expr> | <expr> > <expr>
<expr> ::= <expr> <= <expr> | <expr> >= <expr> <expr> ::= <expr> == <expr> | <expr> != <expr>
<expr> ::= <expr> ˆ <expr> | ( <expr> ) <integer> ::= 1 | 2 | 3 | . . . |∞ <variable> ::= a | b | c | . . . | z
The following is a calculator program,
1 x = 1 0 ; 2 while ( x >= 1 ) { 3 print x ; 4 x = x − 1 ; 5 } .
and its output.
10
9
8
7
6
5
4
3
2
1
Construction of a parse tree requires some preliminary discussion of some constructs and capabilities in C that help facilitate the process.
C O
N F ID
E N
T IA
L D
R A
F T312 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
Listing 10.21: calc3.l.
1 %{ 2 # i n c l u d e "calc3.h"
3 # i n c l u d e "calc3.tab.h"
4 %} 5
6 %option yylineno 7
8 %% 9
10 [ a−z] { /* v a r i a b l e s */ 11 yylval . environI = *yytext − 'a' ; 12 r e t u r n VARIABLE ; } 13
14 0 { yylval . literal = 0 ; 15 r e t u r n INTEGER ; } 16
17 [ 1−9 ] [ 0−9 ] * { /* i n t e g e r s */ 18 yylval . literal = atoi ( yytext) ; 19 r e t u r n INTEGER ; } 20
21 [−^ ( ) < > = + * / ; { } ] { /* s i n g l e−c h a r a c t e r o p e r a t o r s r e t u r n e d a s ←֓ t h e m s e l f */
22 r e t u r n *yytext ; } 23
24 ">=" { /* o t h e r o p e r a t o r s r e t u r n e d a s t o k e n s */ 25 r e t u r n GE ; } 26
27 "<=" r e t u r n LE ; 28 "==" r e t u r n EQ ; 29 "!=" r e t u r n NE ; 30 "while" r e t u r n WHILE ; 31 "if" r e t u r n IF ; 32 "else" r e t u r n ELSE ; 33 "print" r e t u r n PRINT ; 34
35 [ \t\n] { /* i g n o r e w h i t e s p a c e */ ; } 36
37 . yyerror ( "Unknown character") ; 38
39 %% 40
41 i n t yywrap ( void ) { 42 r e t u r n 1 ; 43 }
C O
N F ID
E N
T IA
L D
R A
F T10.4. PUTTING IT ALL TOGETHER: TOWARDS INTERPRETERS 313
Listing 10.22: calc3.y. 1 %{ 2 # i n c l u d e < s t d l i b . h> 3 # i n c l u d e < s t d i o . h> 4 # i n c l u d e < s t d a r g . h> /* p r o v i d e s a c c e s s t o t h e v a r i a b l e argument macros */ 5 # i n c l u d e "calc3.h" 6 # d e f i n e SIZE 26 7 8 PTnode* newOperatorNode( i n t oper , i n t nops , . . . ) ; 9 PTnode* newLiteralOrVariableNode( i n t literalOrVariable , PTnodeFlag flag) ;
10 void freePTnode( PTnode* nodePtr) ; 11 i n t dfs( PTnode* nodePtr) ; 12 13 void yyerror( c h a r * s) ; 14 15 i n t environment[ SIZE ] ; /* e n v ir o n m e n t */ 16 17 e x t e r n i n t yylineno ; 18 19 %} 20 21 /* v a l u e s t a c k w i l l be an a r r a y o f t h e s e YYSTYPE ' s */ 22 %union { 23 i n t literal ; /* l i t e r a l v a l u e */ 24 c h a r environI ; /* e n v ir o n m e n t in de x */ 25 PTnode* nodePtr ; /* node p o i n t e r */ 26 } ; 27 /* g e n e r a t e s t h e f o l l o w i n g : 28 29 t y p e d e f union { 30 i n t l i t e r a l ; 31 c h a r e n v i r o n I ; 32 PTnode * n o de P t r ; 33 } YYSTYPE ; 34 e x t e r n YYSTYPE y y l v a l ; 35 */ 36 /* i n o t h e r words , c o n s t a n t s , v a r i a b l e s , and nodes can 37 be r e p r e s e n t e d by y y l v a l i n t h e p a r s e r ' s v a l u e s t a c k */ 38 39 /* b in ds INTEGER t o i V a l u e i n t h e YYSTYPE union */ 40 /* a s s o c i a t e s t o k e n names w it h c o r r e c t component o f t h e YYSTYPE union */ 41 /* t o g e n e r a t e f o l l o w i n g code */ 42 /* y y l v a l . n o de P t r = n e w Lit e r a lO r V a r i a b l e N o d e ( yyvsp [ 0 ] . l i t e r a l ) ; */ 43 44 %token <literal> INTEGER 45 %token <environI> VARIABLE 46 %token WHILE IF PRINT 47 /* b in ds e x p r t o n o de P t r i n t h e YYSTYPE union */ 48 %type <nodePtr> stmt expr stmtlist 49 50 %nonassoc IFX 51 %nonassoc ELSE 52 %left GE LE EQ NE '>' '<' 53 %left '+' '-' 54 %left '*' '/' 55 %right '^' 56 %nonassoc UMINUS 57 58 %% 59 60 program : code { exit ( 0 ) ; } 61 ; 62 63 code : code stmt { dfs ( $2) ; freePTnode( $2 ) ; } 64 | /* NULL */ 65 66 stmt : ';' { $$ = newOperatorNode( ';' , 2 , NULL , NULL) ; } 67 | expr ';' { $$ = $1 ; } 68 | PRINT expr ';' { $$ = newOperatorNode( PRINT , 1 , $2) ; } 69 | VARIABLE '=' expr ';' { $$ = newOperatorNode( '=' , 2 , 70 newLiteralOrVariableNode( $1 , variableFlag) , $3) ; } 71 | WHILE '(' expr ')' stmt { $$ = newOperatorNode( WHILE , 2 , $3 , $5) ; } 72 | IF '(' expr ')' stmt %prec IFX { $$ = newOperatorNode( IF , 2 , $3 , $5) ; } 73 | IF '(' expr ')' stmt ELSE stmt { $$ = newOperatorNode( IF , 3 , $3 , $5 , $7 ) ; } 74 | '{' stmtlist '}' { $$ = $2 ; } 75 ; 76
C O
N F ID
E N
T IA
L D
R A
F T314 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
Takes advantage of the fact that ints and chars are represented inter- nally as ints.
10.4.2 Helpful C Constructs and Capabilities
We construct the parse tree in a bottom-up fashion. This means that we allocate leaf nodes when variables and integers are reduced. We allocate an internal nodes when operators are reduced. An internal node contains the operator, the number of arguments, and pointers to previously allocated nodes which represent its operands. Two issues arise.
1. We have different types of nodes: internal nodes and leaf nodes, each with different storage requirements.
2. We have multiple types of internal, operator nodes: those for unary, binary, and ternary operators.
We use unions in C and the control C affords the programmer in lay- ing out the memory structures on the help to address the hetergenity of the different types of nodes (i.e., the first issue), and we use functions of variable arguments to help allocate and load internal nodes which have a different number of children pointers depending on the arity of the opera- tor each represents (i.e., the second issue).
unions
1 union { 2 i n t i ; 3 f l o a t f ; 4 c h a r [ 1 6 ] s ; 5 }
Variable Argument Lists
1 void f( i n t nargs , . . . ) { 2 /* t h e d e c l a r a t i o n . . . 3 can o n l y ap p e ar a t t h e end o f an argument l i s t */ 4
5 i n t i , tmp ;
C O
N F ID
E N
T IA
L D
R A
F T10.4. PUTTING IT ALL TOGETHER: TOWARDS INTERPRETERS 315
PTnodeFlag flag
int oper
int nops
pointer to an array of pointers of type
PTnode*
OperatorNode
structPTnode
int literalOrVariable union − could be any 1 of 2
OperatorNode operator1
PTnode** operands
Figure 10.21: structures for parse tree nodes in calculator (version 3).
6
7 va_list ap ; /* argument p o i n t e r */ 8
9 va_start( ap , narags) ; /* i n i t i a l i z e s ap t o p o i n t t o t h e 10 f i r s t unnamed argument ; 11 v a _ s t a r t must be c a l l e d once 12 b e f o r e ap can be used */ 13
14 f o r ( i= 0 ; i < nargs ; i++) 15 temp = va_arg( ap , i n t ) ; /* r e t u r n s one argument and 16 s t e p s ap t o t h e n e x t argument */ 17 /* t h e s e co n d argument t o v a _ a r g 18 must be a t y p e name s o t h a t 19 v a _ a r g s knows how b i g a s t e p 20 t o t a k e */ 21
22 va_end( ap ) ; /* c l e a n−up ; must be c a l l e d b e f o r e 23 f u n c t i o n r e t u r n s */ 24 }
10.4.3 Structures for Parse Tree Nodes
Header File
We place the datatype definitions for our parse tree in a file named calc.h.
10.4.4 Precedence and Associativity in Calculator (version 3)
C O
N F ID
E N
T IA
L D
R A
F T316 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
PTnode* newLiteralOrVariableNode(int literalOrVariable, PTnodeFlag flag) {
/* copy data */
100
}
called when we see a literal or variable; creates a leaf node in parse tree
PTnode* nodePtr 100
PTnodeFlag flag
int literalOrVariable
Figure 10.22: Node type used for literals and variables (i.e., leaf nodes) in calculator (ver- sion 3).
PTnode* nodePtr
va_list ap
100100
int operatorLiteral
int numOfOperands
PTnode** operands
called when we see an operator; creates an internal node in parse tree
PTnodeFlag flag
PTnode* newOperatorNode(int operatorLiteral, int numOfOperands, ... ) {
}
/* copy data */
OperatorNode operator1
Figure 10.23: Node type used for operators (i.e., internal nodes) in calculator (version 3).
C O
N F ID
E N
T IA
L D
R A
F T10.4. PUTTING IT ALL TOGETHER: TOWARDS INTERPRETERS 317
1 /* v a l u e s t a c k w i l l be an a r r a y o f t h e s e YYSTYPE ' s ; 2 h as n o t h i n g t o do w it h t h e union i n c a l c 3 . h */ 3 %union { 4 i n t literal ; /* i n t e g e r v a l u e */ 5 c h a r environI ; /* e n vir o n me n t i n d e x */ 6 PTnode* nodePtr ; /* node p o i n t e r */ 7 } ; 8 /* g e n e r a t e s t h e f o l l o w i n g : 9
10 t y p e d e f union { 11 i n t l i t e r a l ; 12 c h a r e n v i r o n I ; 13 n o d e P t r * n o d e P t r ; 14 } YYSTYPE ; 15 e x t e r n YYSTYPE y y l v a l ; 16
17 i n o t h e r words , c o n s t a n t s , v a r i a b l e s , and nodes can 18 be r e p r e s e n t e d by y y l v a l i n t h e p a r s e r ' s v a l u e s t a c k 19
20 b i n d s INTEGER t o i V a l u e i n t h e YYSTYPE union 21 a s s o c i a t e s t o k e n names w it h c o r r e c t component o f t h e 22 YYSTYPE union t o g e n e r a t e f o l l o w i n g code 23 y y l v a l . n o d e P t r = n e w L i t e r a l O r V a r i a b l e N o d e ( yyvsp [ 0 ] . l i t e r a l ) ; */ 24
25 %token <literal> INTEGER 26 %token <environI> VARIABLE 27 %token WHILE IF PRINT 28 %nonassoc IFX 29 %nonassoc ELSE 30
31 %left GE LE EQ NE '>' '<' 32 %left '+' '-' 33 %left '*' '/' 34 %right '^' 35 %nonassoc UMINUS 36
37 /* b i n d s e x p r t o n P t r i n t h e YYSTYPE union */ 38 %type <nodePtr> stmt expr stmtlist
10.4.5 Interpreters: Program Evaluators
When the syntax tree is completely built, pass only a pointer to the root node to a function eval which interprets the program and prints any out- put. The eval function returns an int and conducts a depth-first traversal of the tree. Since the tree is constructed in a bottom-up fashion, the depth- first walk visits nodes in the order in which they were allocated. This ap-
C O
N F ID
E N
T IA
L D
R A
F T318 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
interpreter.o
interpreter.c calc.tab.h calc.h
parsetree.o
parsetree.c
calc.tab.o
calc.tab.c
calc.y
compiler.o
compiler.c
lex.yy.o
lex.yy.c
calc.l
all
interpreter compilerparsetree
Figure 10.24: Makefile dependency graph for calculator (version 3).
proach has the attractive property of applying the operators in the order that they were encountered during parsing or, in other words, according to the rules of precedence.
When eval returns, pass only a pointer to the root node of the syntax tree to a function freeTree which frees each node of the tree.
The Makefile dependency graph for calculator (version 3) is given in Fig. 10.24.
10.4.6 Conceptual Exercises for Section 10.4
Exercise 10.4.1: What is the underlying cause of a shift-reduce conflict?
Exercise 10.4.2: What is the underlying cause of a reduce-reduce conflict?
Exercise 10.4.3: What does bison do when it encounters a shift-reduce conflict?
Exercise 10.4.4: What action does bison take when it encounters a shift- reduce conflict?
Exercise 10.4.5: What does bison do when it encounters a reduce-reduce conflict?
Exercise 10.4.6: Give a specific example of a shift-reduce conflict. Show the complete grammar, input string, parse stack, and value stack to clearly il-
C O
N F ID
E N
T IA
L D
R A
F T10.4. PUTTING IT ALL TOGETHER: TOWARDS INTERPRETERS 319
Listing 10.23: Makefile for calculator (version 3).
1 SRC = calc3 2 CC = gcc −g 3 LEX = flex 4 LEX_FLAGS = 5 YACC = bison 6 YACC_FLAGS = −d −t 7
8 all : interpreter compiler parsetree 9 # a l l : i n t e r p r e t e r c o m p i l e r
10
11 interpreter : lex . yy . o $ ( SRC) . tab . o interpreter . o 12 $ ( CC) −lm lex . yy . o $ ( SRC) . tab . o interpreter. o −o interpreter 13
14 compiler : lex . yy . o $ ( SRC) . tab . o compiler . o 15 $ ( CC) lex . yy . o $ ( SRC) . tab . o compiler . o −o compiler 16
17 parsetree : lex . yy . o $ ( SRC) . tab . o parsetree . o 18 $ ( CC) lex . yy . o $ ( SRC) . tab . o parsetree . o −o parsetree 19
20 lex . yy . o : lex . yy . c $ ( SRC) . tab . h $ ( SRC) . h 21 $ ( CC) −c lex . yy . c 22
23 lex . yy . c : $ ( SRC) . l 24 $ ( LEX) $ ( LEX_FLAGS) $ ( SRC) . l 25
26 $ ( SRC) . tab . o : $( SRC) . tab . c $( SRC) . h 27 $ ( CC) −c $ ( SRC) . tab . c 28
29 $ ( SRC) . tab . c : $( SRC) . y 30 $ ( YACC) $ ( YACC_FLAGS) $ ( SRC) . y 31
32 $ ( SRC) . tab . h : $( SRC) . y 33 $ ( YACC) $ ( YACC_FLAGS) $ ( SRC) . y 34
35 interpreter . o : interpreter . c $ ( SRC) . h $( SRC) . tab . h 36 $ ( CC) −c interpreter . c 37
38 compiler . o : compiler . c $( SRC) . h $ ( SRC) . tab . h 39 $ ( CC) −c compiler . c 40
41 parsetree . o : parsetree . c $( SRC) . h $( SRC) . tab . h 42 $ ( CC) −c parsetree . c 43
44 clean : 45 −rm * . o $ ( SRC) . tab . h $( SRC) . tab . c lex . yy . c interpreter compiler ←֓
parsetree
C O
N F ID
E N
T IA
L D
R A
F T320 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
lustrate the conflict and to convince us that you know what you are talking about.
Exercise 10.4.7: Give a specific example of a shift-reduce conflict. Show a complete BNF grammar, input string, and parse stack to clearly illustrate the conflict. Use . (dot) to denote the top of the stack.
Exercise 10.4.8: Give a specific example of a reduce-reduce conflict. Show the complete grammar, input string, parse stack, and value stack to clearly illustrate the conflict and to convince us that you know what you are talk- ing about.
Exercise 10.4.9: Give a specific example of a reduce-reduce conflict. Show a complete BNF grammar, input string, parse stack, and value stack to clearly illustrate the conflict. Use . (dot) to denote the top of the stack.
Exercise 10.4.10: Consider the following context-free grammar in EBNF. Would this grammar pose a problem bison, even without directives to disambiguate the grammar? Explain why or why not. Be specific.
<stmt> ::= if <expr> <stmt>
<stmt> ::= if <expr> <stmt> else <stmt> <stmt> ::= s <expr> ::= c
Exercise 10.4.11: State whether it is preferable or not to use a left-recursive or right-recursive grammar with bison and why. Explain. Be specific.
Exercise 10.4.12: Consider the following ambiguous context-free grammar in EBNF for the dangling else problem. Does this grammar as is, and without directives to disambiguate the grammar, pose a problem for bison? Explain why or why not. Be specific.
<stmt> ::= if <expr> <stmt>|<matched_stmt> <matched_stmt> ::= if <expr> <matched_stmt> else <stmt> <matched_stmt> ::= <other>
where the non-terminal <other> generates some non-if statement such as a print statement.
C O
N F ID
E N
T IA
L D
R A
F T10.4. PUTTING IT ALL TOGETHER: TOWARDS INTERPRETERS 321
Exercise 10.4.13: In version 1 of the calculator, why is the string print -4 - 5 parsed as a sentence, if the unary minus operator has the highest precedence?
Exercise 10.4.14: In version 2 of the calculator, will the ’-’ expr %prec ’ˆ’ { $$ = $2*-1; } rule interfere with parsing the string print 2 ˆ -3;
Exercise 10.4.15: In version 2 of the calculator, what is the difference between ’-’ expr %prec ’ˆ’ { $$ = $2*-1; } and ’-’ expr %prec UMINUS { $$ = $2*-1; }?
10.4.7 Programming Exercises for Section 10.4
Exercise 10.4.16: Consider the following context-free grammar defined in EBNF (from [Lou02]):
<expr> ::= ( <list> ) | a <list> ::= <expr> [<list>]
where <expr> and <list> are non-terminals and a, (, and ) are terminals.
Automatically generate a shift-reduce, bottom-up parser by defining a flex and a bison specification of a parser for the language defined by this grammar. The parser must accepts strings from standard input (one per line) until EOF and determines whether or not each string is in the lan- guage defined by this grammar. Thus, it might be help to think of defining this language using the following context-free grammar in EBNF:
<sentence> ::= <sentence> <expr> \n | <expr> \n <expr> ::= (<list>) | a <list> ::= <expr> | <expr> <list>
where <sentence>, <expr>, and <list> are non-terminals and a, (, ), and \n are terminals.
Factor your program into a scanner (lexical analyzer) and shift-reduce parser (syntactic analyzer) as shown in Figs. 10.3 and 10.5.
C O
N F ID
E N
T IA
L D
R A
F T322 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
You may not assume that each lexeme will be valid and separated by ex- actly one space, or that each line will contain no leading or trailing whites- pace. There are two distinct error conditions that your program must recognize. First, if a given string does not consist of valid lexemes, then respond with this message: “...” contains invalid lexemes and, thus, is not a sentence. Second, if a given string consists of valid lexemes but it is not a sentence according to the grammar, then re- spond with the message: “...” is not a sentence. Note that the “invalid lexemes” message takes priority over the “not a sentence” mes- sage (i.e., the “not a sentence” message can only be issued if the input string consists entirely of valid lexemes).
You may assume that whitespace is ignored, that no line of input will ex- ceed 4,096 characters, that each line of input will end with a newline, and that no string will contain more than 200 lexemes.
Print only one line of output to standard output per line of input, and do not prompt for input. The following is a sample interactive session with the parser (> is simply the prompt for input and will be the empty string in your system):
> ( a)
"( a )" is a sentence.
> a
"a" is a sentence.
> ( ( ( a a ) ) )
"( ( ( a a ) ) )" is a sentence.
> ( a ) )
"( a ) )" is not a sentence.
> ,(a)
",(a)" contains invalid lexemes and, thus, is not a sentence.
> (( (a a ) ))
"( ( ( a a ) ) )" is a sentence.
> ( a ( a ) ) )
"( a ( a ) ) )" is not a sentence.
> (( a ) 1 )
"(( a ) 1 )" contains invalid lexemes and, thus, is not a sentence.
> (a(a))
"( a ( a ) )" is a sentence.
> ( ( a ) )
"( ( a ) )" is a sentence.
> ( )
"( )" is not a sentence.
C O
N F ID
E N
T IA
L D
R A
F T10.4. PUTTING IT ALL TOGETHER: TOWARDS INTERPRETERS 323
> (
"(" is not a sentence.
You may assume the following code in your bison specification, though you must replace each ... with one line of code:
1 sentence : sentence expr '\n' { printf ( "\"%s\" is a sentence.\n" , 2 temp) ; 3 . . . } 4 | error '\n' { printf ( "\"%s\" is not a sentence.\n" , 5 temp) ; 6 . . . 7 yyclearin ; /* d i s c a r d lo o k ah e ad */ 8 yyerrok ; } 9 |
10 ; 11 /* b i s o n s p e c i f i c a t i o n f i l e p a r s e r . y */
Also write a Makefile which builds your parser. Your Makefile must include target directives for every derived file produced during the com- pilation process (i.e., each program, each object file, and any other inter- mediate files produced during code generation and compilation). Make sure that each directive also lists all files on which the derived file depends in its dependency list. Also, your Makefile must be written to carry out only the commands necessary to bring any produced file up-to-date. Your Makefile must do just enough, but no extra, work to bring the final exe- cutable for your parser up-to-date every time make is invoked. In addition, it must have an all directive and a clean directive to remove all gener- ated files. Use variables where appropriate to improve the readability of your Makefile. Your Makefile must bring everything up-to-date, using only flex, bison, and gcc, without any warnings or errors, when make is invoked.
Exercise 10.4.17: Consider the following context-free grammar defined in EBNF:
<P> ::= () | (<P>) | ()(<P>) | (<P>)<P>
where <P> is a non-terminal and ( and ) are terminals.
C O
N F ID
E N
T IA
L D
R A
F T324 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
Complete Programming Exercise 10.4.16 using this grammar subject to all of the requirements given in that exercise.
The following is a sample interactive session with the parser:
> ()
"()" is a sentence.
> ()()
"()()" is a sentence.
> (())
"(())" is a sentence.
> (()())()
"(()())()" is a sentence.
> ((()())())
"((()())())" is a sentence.
> (a)
"(a)" contains invalid lexemes and, thus, is not a sentence.
> )(
")(" is not a sentence.
> )()
")()" is not a sentence.
> )()(
")()(" is not a sentence.
> (()()
"(()()" is not a sentence.
> ())((
"())((" is not a sentence.
> ((()())
"((()())" is not a sentence.
Exercise 10.4.18: Consider the following context-free grammar defined in EBNF from § 10.3.3:
<program> ::= <program> <expr> \n | <expr> \n <expr> ::= <expr> + <expr>
<expr> ::= <expr> * <expr> <expr> ::= − <expr>
<expr> ::= <integer> <integer> ::= 1 | 2 | 3 | . . . |∞
where <expr> and <integer> are non-terminals and +, *, −, and 1, 2, 3, . . . are terminals.
Use flex and bison to build a C program which reads sentences in the language defined by this grammar from standard input (one per line) until
C O
N F ID
E N
T IA
L D
R A
F T10.4. PUTTING IT ALL TOGETHER: TOWARDS INTERPRETERS 325
EOF and writes each expression evaluated and decorated with parentheses to indicate the order of operator application to standard output (using the format below, one per line). Normal precedence rules hold: − has the high- est, * has the second highest, and + has the lowest. Assume left-to-right associativity. The following is sample input and output for the expression evaluator (> is simply the prompt for input and will be the empty string in your system):
> 2+3*4
(2+(3*4)) = 14
> 2+3*-4
(2+(3*(-4))) = -10
> -2*3+4
(((-2)*3)+4) = -2
Do not build a parse tree to solve this problem.
Hint: Use an array implementation of a stack which contains elements of type char*. Also, use the sprintf function to convert an integer to a string. For example,
1 c h a r * string_representation_of_an_integer = 2 malloc ( 1 0 * s i z e o f ( * string_representation_of_an_integer) ) ; 3
4 /* p r i n t s t h e i n t e g e r 789 t o 5 t h e s t r i n g v a r i a b l e s t r i n g _ r e p r e s e n t a t i o n _ o f _ a n _ i n t e g e r */ 6 sprintf ( string_representation_of_an_integer, "%d" , 7 8 9 ) ; 7
8 /* n e x t l i n e p r i n t s t h e i n t e g e r 789 t o s t d o u t */ 9 printf ( "%s" , string_representation_of_an_integer) ;
You must explicitly deallocate any memory you explicitly allocate (i.e., your program must not have any memory leaks).
Write a Makefile which builds your expression evaluator. Your Makefile must include target directives for every derived file produced during the compilation process (i.e., each program, each object file, and any other intermediate files produced during code generation and com- pilation). Make sure that each directive also lists all files on which the derived file depends in its dependency list. Also, your Makefile must be written to carry out only the commands necessary to bring any produced
C O
N F ID
E N
T IA
L D
R A
F T326 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
file up-to-date. Your Makefile must do just enough, but no extra, work to bring the final executable for your evaluator up-to-date every time make is invoked. In addition, it must have an all directive and a clean directive to remove all generated files. Use variables where appropriate to improve the readability of your Makefile. Your Makefile must bring everything up-to-date, using only flex, bison, and gcc, without any warnings or errors, when make is invoked.
Exercise 10.4.19: Build a parser to determine the order in which operators of a logical expression are evaluated. Expressions are defined by the fol- lowing context-free grammar in BNF (not EBNF):
<expr> ::= <expr> & <expr>
<expr> ::= <expr> | <expr> <expr> ::= ∼ <expr>
<expr> ::= <literal> <literal> ::= t
<literal> ::= f
where t, f, |, &, and ∼ are terminals which represent true, false, or, and, and not, respectively. The following is sample input and output for the expression evaluator (> is simply the prompt for input and will be the empty string in your system).
> f | t & f | ~t
((f | (t & f)) | (~t)) is false.
> ~t | t | ~f & ~f & t & ~t | f
((((~t) | t) | ((((~f) & (~f)) & t) & (~t))) | f) is true.
Notice that you must decorate the parsed expression with parentheses to indicate the order of operator-execution as well as evaluate it. Normal precedence rules hold: ∼ has the highest, & has the second highest, and | has the lowest. Assume left-to-right associativity.
Requirements:
a) Your program must read from standard input and write to standard output. Specifically, your program must read a set of expressions from standard input (one per line) and write the corresponding parenthesized
C O
N F ID
E N
T IA
L D
R A
F T10.4. PUTTING IT ALL TOGETHER: TOWARDS INTERPRETERS 327
expressions (also one per line, in the format used above) to standard output.
b) Write a Makefile as indicated in Programming Exercise 10.4.18.
Exercise 10.4.20: Add a do {...} while (...); loop to the calculator (version 3).
Exercise 10.4.21: Re-instrument version 3 of the calculator so that the integer representing a literal or variable in the PTnode type is wrapped in a struct called LiteralOrVariableNode. Call this approach version 4.
PTnodeFlag flag
PTnode*
int literalOrVariable
structPTnode
union − could be any 1 of 2 LiteralOrVariable literalOrVariable
OperatorNode operator1
OperatorNode
pointer to an array of pointers of type
LiteralOrVariableNode
int oper
int nops
PTnode** operands
LiteralOrVariableNode literalOrVariable
/* copy data */
100
}
called when we see a literal or variable; creates a leaf node in parse tree
PTnode* nodePtr 100
PTnodeFlag flag
PTnode* newLiteralOrVariableNode(int literalOrVariable, PTnodeFlag flag) {
C O
N F ID
E N
T IA
L D
R A
F T328 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
Exercise 10.4.22: Re-instrument version 4 of the calculator created in Programming Exercise 10.4.21 to factor the LiteralOrVariableNode struct into a LiteralNode struct and a VariableNode struct. Similarly, factor the newLiteralOrVariableNode function into newLiteralNode and newVariableNode functions. Call this ap- proach version 5.
PTnodeFlag flag
int variable
VariableNode
PTnode*
structPTnode
OperatorNode
LiteralNode
int literal
− could be any 1 of 3union
OperatorNode operator1
VariableNode variable
LiteralNode literal
int oper
int nops
pointer to an array of pointers of type
called a "variant record"
PTnode** operands
LiteralNode literal
/* copy data */
100
}
PTnode* nodePtr 100
PTnodeFlag flag
called when we see a literal; creates a leaf node in parse tree
PTnode* newLiteralNode(int literal, PTnodeFlag flag) {
C O
N F ID
E N
T IA
L D
R A
F T10.4. PUTTING IT ALL TOGETHER: TOWARDS INTERPRETERS 329
VariableNode variable
/* copy data */
100
}
PTnode* nodePtr 100
PTnodeFlag flag
called when we see a variable; creates a leaf node in parse tree
PTnode* newVariableNode(int variable, PTnodeFlag flag) {
Exercise 10.4.23: Re-instrument version 3 of the calculator to use a dif- ferent design for the OperatorNode struct. Specifically, instead of an a pointer to an array of type PTnode*, make the operands field of the OperatorNode struct be a array of size one of pointers of type PTnode* (as shown below) and dynamically expand it as needed in the newOperatorNode function. Call this approach version 6.
PTnodeFlag flag
int oper
int nops
OperatorNode
structPTnode
int literalOrVariable union − could be any 1 of 2
OperatorNode operator1
(expandable)PTnode* operands[1]
C O
N F ID
E N
T IA
L D
R A
F T330 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
PTnode* nodePtr
va_list ap
100 100
called when we see an operator; creates an internal node in parse tree
PTnodeFlag flag
int operatorLiteral
int numOfOperands
PTnode* operands[1]
(expandable)
PTnode* newOperatorNode(int operatorLiteral, int numOfOperands, ... ) {
}
OperatorNode operator1
Would this approach work if the union was the first field of the PTnode struct rather than the PTnodeFlag enum? Explain.
Exercise 10.4.24: Re-instrument version 4 of the calculator (i.e., Program- ming Exercise 10.4.21) to use the memory design of version 6 (i.e., Pro- gramming Exercise 10.4.23). Call this approach version 7.
Exercise 10.4.25: Re-instrument version 5 of the calculator (i.e., Program- ming Exercise 10.4.22) to use the memory design of version 6 (i.e., Pro- gramming Exercise 10.4.23). Call this approach version 8.
Exercise 10.4.26: Re-instrument version 7 of the calculator (i.e., Program- ming Exercise 10.4.24) to use the memory design depicted below where a the PTnode type is a union of structs rather than a struct containing a union. Call this approach version 9 (a memory overlay approach).
C O
N F ID
E N
T IA
L D
R A
F T10.4. PUTTING IT ALL TOGETHER: TOWARDS INTERPRETERS 331
PTnodeFlag flag
union of struct sPTnode
OperatorNode operator1
(expandable)PTnode* operands[1]
OperatorNode
int oper
int nops
nodeFlag flag
union − could be any 1 of 3LiteralOrVariableNode literalOrVariable
PTnodeFlag flag
LiteralOrVariableNode
int literalOrVariable
Would this approach work if the nodeFlag enum type was not a mem- ber of both the LiteralOrVariableNode and OperatorNode struct types, in addition to being a member of the PTnode struct type? Ex- plain. Would this approach work if the PTnodeFlag enum was the last member of the PTnode union? Explain.
Exercise 10.4.27: Re-instrument version 8 of the calculator (i.e., Program- ming Exercise 10.4.25) to use the memory design depicted in version 9 (i.e., Programming Exercise 10.4.27). Call this approach version 10.
C O
N F ID
E N
T IA
L D
R A
F T332 CHAPTER 10. AUTOMATIC PROGRAM GENERATION
PTnodeFlag flag
union of struct sPTnode
(expandable)PTnode* operands[1]
OperatorNode
int oper
int nops
nodeFlag flag
LiteralNode
int literal
VariableNode
int variable
union − could be any 1 of 4
OperatorNode operator1
VariableNode variable
LiteralNode literal
PTnodeFlag flag
PTnodeFlag flag
Exercise 10.4.28:
Exercise 10.4.29: Build a graphical user interface in Qt, akin to that shown below, for the interpreter/compiler developed in Programming Project 10.5. See http://hipersayanx.blogspot.com/2013/03/ using-flex-and-bison-with-qt.html for help on using flex and bison with Qt.