Скачать презентацию Compiler Tools Lex Yacc Flex Bison Скачать презентацию Compiler Tools Lex Yacc Flex Bison

b53498220b9a454ed94d867782f3f6b6.ppt

  • Количество слайдов: 55

Compiler Tools Lex/Yacc – Flex & Bison Compiler Tools Lex/Yacc – Flex & Bison

Compiler Front End (from Engineering a Compiler) Source code Scanner tokens Parser Intermediate Representation Compiler Front End (from Engineering a Compiler) Source code Scanner tokens Parser Intermediate Representation Errors Scanner (Lexical Analyzer) • Maps stream of characters into words Basic unit of syntax x = x + y ; becomes Speed is an issue in scanning use a specialized recognizer • The actual words are its lexeme • Its part of speech (or syntactic category) is called its token type • Scanner discards white space & (often) comments

The Front End (from Engineering a Compiler) Source code Scanner tokens Parser IR Parser The Front End (from Engineering a Compiler) Source code Scanner tokens Parser IR Parser Errors • Checks stream of classified words (parts of speech) for grammatical correctness • Determines if code is syntactically well-formed • Guides checking at deeper levels than syntax • Builds an IR representation of the code Parsing is harder than scanning. Better to put more rules in scanner (whitespace etc).

The Big Picture • Language syntax is specified with parts of speech, not words The Big Picture • Language syntax is specified with parts of speech, not words • Syntax checking matches parts of speech against a grammar 1. goal expr S = goal 2. expr op term 3. | term T = { number, id, +, - } 4. term number 5. | id 6. op 7. N = { goal, expr, term, op } P = { 1, 2, 3, 4, 5, 6, 7} + | – No words here! Parts of speech, not words!

Why study lexical analysis? • We want to avoid writing scanners by hand • Why study lexical analysis? • We want to avoid writing scanners by hand • Finite automata are used in other applications: grep, website filtering, various “find” commands source code Scanner parts of speech & words tables or code specifications Goals: Scanner Generator Specifications written as “regular expressions” Represent words as indices into a global table To simplify specification & implementation of scanners To understand the underlying techniques and technologies

Finite Automata Formally a finite automata is a five-tuple(S, , , s 0, SF) Finite Automata Formally a finite automata is a five-tuple(S, , , s 0, SF) where • S is the set of states, including error state Se. S must be finite. • is the alphabet or character set used by recognizer. Typically union of edge labels (transitions between states). • (s, c) is a function that encodes transitions (i. e. , character c in changes to state s in S. ) • s 0 is the designated start state • SF is the set of final states, drawn with double circle in transition diagram

Finite Automata Finite automata to recognize fee and fie: e e S 0 f Finite Automata Finite automata to recognize fee and fie: e e S 0 f S 1 S 3 S 2 i S 4 e n S = {s 0, s 1, s 2, s 3, s 4, s 5, se} n = {f, e, i} n (s, c) set of transitions shown above n s 0 = s 0 n S 5 SF= { s 3, s 5} Set of words accepted by a finite automata F forms a language L(F). Can also be described by regular expressions.

Finite Automata Quick Exercise n Draw a finite automata that can recognize CU | Finite Automata Quick Exercise n Draw a finite automata that can recognize CU | CSM | DU (drawing included below for reference) e e S 0 f S 1 S 3 S 2 i S 4 e S 5

RE vs CFG* n Context-free grammars are strictly more powerful than regular expressions. Any RE vs CFG* n Context-free grammars are strictly more powerful than regular expressions. Any language that can be generated using regular expressions can be generated by a context-free grammar. ¨ There are languages that can be generated by a context-free grammar that cannot be generated by any regular expression. ¨ n n As a corollary, CFGs are strictly more powerful than DFAs and NDFAs. The proof is in two parts: Given a regular expression R , we can generate a CFG G such that L(R) == L(G). ¨ We can define a grammar G for which there is no FA F such that L(F) == L(G). ¨ * from http: //www. cs. rochester. edu/~nelson/courses/csc_173/grammars/cfg. html

Example n n No finite automata (and therefore no regular expression) can recognize the Example n n No finite automata (and therefore no regular expression) can recognize the set of strings consisting of some number of 0 s followed by the same number of 1 s. {0^n 1^n|n>=1} --> 0 1 --> 01 What does this mean for us? Recognizing tokens can be done with a regular expression, but we need a CFG to recognize sentences in our language.

Regular Expression Examples n n digit: [0 -9] int with at least 1 digit: Regular Expression Examples n n digit: [0 -9] int with at least 1 digit: [0 -9]+ int that can have 0 digits: [0 -9]* What about float? ¨ [0 -9]*. [0 -9]+ // literal. , at least 1 digit after. ¨ – what about 0 or 2? ¨ ([0 -9]+)| ([0 -9]*. [0 -9]+) // combine int and float, notice use of (), ¨ - what about unary -? ¨ -? (([0 -9]+)| ([0 -9]*. [0 -9]+))

Regular Expressions in Lex* The characters that form regular expressions include: Ø. matches any Regular Expressions in Lex* The characters that form regular expressions include: Ø. matches any single character except newline Ø * matches zero or more copies of preceding expression Ø [] a character class that matches any character within the brackets. If first character is ^ will match any character except those within brackets. A dash can be used for character range, e. g. , [0 -4] is equivalent to [01234]. more in book… Ø Examples: a. [0]* would match ab, ac, aa, ab 0, ac 0, aa 0, ab 00, ac 00, aa 00, … Ø a[bc] would match ab or ac Ø a[bc]* would match a, ab, ac, abb, acc, abc, acb, … Ø * from lex & yacc by Levine, Mason & Brown

Regular Expressions in Lex* The characters that form regular expressions also include: Ø ^ Regular Expressions in Lex* The characters that form regular expressions also include: Ø ^ matches beginning of line as first character of expression (also negation within [], as listed above). Ø $ matches end of line as last character of expression Ø {} indicates how many times previous pattern is allowed to match, e. g. , A{1, 3} matches one to three occurrences of A. Ø used to escape metacharacters, e. g. , * is literal asterisk, ” is a literal quote, { is literal open brace, etc. Ø Examples: ^I[ab] matches Ia or Ib at the beginning of a line Ø I[ab]$ matches Ia or Ib at the end of a line, e. g. , not Iaj Ø. . . matches a. b, c. d etc. Ø * from lex & yacc by Levine, Mason & Brown

Regular Expressions, continued Ø Ø Ø + matches one or more occurrences of preceding Regular Expressions, continued Ø Ø Ø + matches one or more occurrences of preceding expression, e. g. , [0 -9]+ matches “ 1” “ 11” or “ 1234” but not empty string ? matches zero or one occurrence of preceding expression, e. g. , ? [0 -9]+ matches signed number with optional leading minus sign | matches either preceding or following expression, e. g. , cow|pig|sheep matches any of the three words “…” interprets everything inside quotation marks literally () Groups a series of regular expressions into a new regular expression, e. g. , (01) becomes character sequence 01. Useful when building up complex patterns with *, + and |. Examples: Ø a[0 -9]+(dog|cat) matches a 0 cat, a 21 dog, etc. but NOT acat

More Regular Expression Examples n What’s a regular expression for matching quotes? ¨ Try: More Regular Expression Examples n What’s a regular expression for matching quotes? ¨ Try: ”. *” ¨ won’t work for lines with multiple quoted parts, such as: “mine” and “yours” because lex matches largest possible pattern. ¨ ”[^”n]*[“n] ¨ will work by excluding “ (forces lex to stop as soon as “ is reached). The n keeps a quoted string from exceeding one line.

Flex – Fast Lexical Analyzer Here’s where we’ll put the regular expressions to good Flex – Fast Lexical Analyzer Here’s where we’ll put the regular expressions to good use! lex. yy. c contains yylex() regular expressions & C-code rules FLEX (Scanner generator) scanner (program to recognize patterns in text) compile executable – analyzes and executes input

Flex input file n 3 sections definitions %% rules %% user code Flex input file n 3 sections definitions %% rules %% user code

Definition Section Examples n name definition DIGIT [0 -9] ID [a-z][a-z 0 -9]* n Definition Section Examples n name definition DIGIT [0 -9] ID [a-z][a-z 0 -9]* n A subsequent reference to {DIGIT}+". "{DIGIT}* is identical to: ([0 -9])+". "([0 -9])*

C Code n Can include C-code in definitions %{ /* This is a comment C Code n Can include C-code in definitions %{ /* This is a comment inside the definition */ #include // may need headers #include // for printf in BB #include // for exit(0) in BB %}

Rules n The rules section of the flex input contains a series of rules Rules n The rules section of the flex input contains a series of rules of the form: pattern action n In the definitions and rules sections, any indented text or text enclosed in %{ and %} is copied verbatim to the output (with the %{ %}'s removed). The %{ %}'s must appear unindented on lines by themselves.

Example: Simple Pascal-like recognizer Definitions section: /* scanner for a toy Pascal -like language Example: Simple Pascal-like recognizer Definitions section: /* scanner for a toy Pascal -like language */ Remember these are on a line %{ by themselves, unindented! /* need for the call to Lines inserted as-is into atof() below */ resulting code #include %} DIGIT [0 -9] Definitions that can be used in rules section ID [a-z][a-z 0 -9]* n } }

Example continued text that matched the pattern (a char*) pattern action n Rules section: Example continued text that matched the pattern (a char*) pattern action n Rules section: %% {DIGIT}+ { printf("An integer: %s (%d)n", yytext, atoi(yytext )); } {DIGIT}+". "{DIGIT}* {printf("A float: %s (%g)n", yytext, atof(yytext)); } if|then|begin|end|procedure|function {printf("A keyword: %sn", yytext); } {ID} { printf( "An identifier: %sn", yytext ); } "+"|"-"|"*"|"/" { printf( "An operator: %sn", yytext ); } "{"[^}n]*"}" /* eat up one-line comments */ [ tn]+ /* eat up whitespace */. { printf( "Unrecognized character: %sn", yytext ); }

Example continued n User code (required for flex, in library for lex) %% yywrap() Example continued n User code (required for flex, in library for lex) %% yywrap() {} // needed to link, unless libfl. a is available // OR put %option noyywrap at the top of a flex file. int main(int argc, char ** argv ) { ++argv, --argc; /* skip over program name */ if ( argc > 0 ) yyin = fopen( input file lex argv[0], "r" ); else yyin = stdin; yylex(); } lexer function produced by lex

Flex exercise #1 1. 2. 3. Download pascal. l From a command prompt (Start->Run->cmd): Flex exercise #1 1. 2. 3. Download pascal. l From a command prompt (Start->Run->cmd): n Flex -opascal. c -L pascal. l n NOTE: without –o option output file will be called lex. yy. c n -L option suppresses #lines that cause problems with some compilers (e. g. Dev. C++) Compile and execute pascal. c (batch on Blackboard) n 4. gcc –opascal. exe –Lc: progra~1gnuwin 32lib pascal. c –lfl -ly Execute program. Type in digits, ids, keywords etc. End program with Ctrl-Z

Flex exercise #2 n n n Copy words. l (from lex & yacc) Use Flex exercise #2 n n n Copy words. l (from lex & yacc) Use flex then compile and execute What does it do? Extend the example with 1 new part of speech. Recognize lexemes R 0 -R 9 as register names Recognize complex numbers, including for example -3+4 i, +5 -6 i, +7 i, 8 i, -12 i, but not 3++4 i (hint: print newline before displaying your complex number, lexer may display 3+ and then recognize +4 i)

Lex techniques n Hardcoding lists not very effective. Often use symbol table. Example in Lex techniques n Hardcoding lists not very effective. Often use symbol table. Example in lec & yacc, not covered in class but see me if you’re interested.

Bison – like Yacc (yet another compiler) Context-free Grammar in BNF form, LALR(1)* Bison Bison – like Yacc (yet another compiler) Context-free Grammar in BNF form, LALR(1)* Bison parser (c program) group tokens according to grammar rules Bison parser provides yyparse *Look. Ahead Left Recursive You must provide: • the lexical analyzer (e. g. , flex) • an error-handling routine named yyerror • a main routine that calls yyparse

Bison Parser n n Same sections as flex (yacc came first): definitions, rules, C-Code Bison Parser n n Same sections as flex (yacc came first): definitions, rules, C-Code We’ll discuss rules first, then definitions and C-Code

Bison Parser – Rule Section n n Consider CFG <statement> => ID = <expression> Bison Parser – Rule Section n n Consider CFG => ID = Would be written in bison “rules” section as: statement: NAME ‘=‘ expression | expression { printf("= %dn", $1); ; } white space ; at end n n expression: NUMBER ‘+’ NUMBER { $$ = $1 + $3; } | NUMBER ‘-’ NUMBER { $$ = $1 + $3; } | NUMBER { $$ = $1; } ; Use : between lhs and rhs, place ; at end. What are $$? next slide… NOTE: The first rule in statement won’t be operational yet…

More on bison Rules and Actions n n $1, $3 refer to RHS values. More on bison Rules and Actions n n $1, $3 refer to RHS values. $$ sets value of LHS. In expression, $$ = $1 + $3 means it sets the value of lhs (expression) to NUMBER ($1) + NUMBER ($3) A rule action is executed when the parser reduces that rule (will have recognized both NUMBER symbols) lexer should have returned a value via yylval (next slide) when is this statement: NAME ‘=‘ expression executed? | expression { printf("= %dn", $1); } ; $$ $1 $2 $3 expression: NUMBER ‘+’ NUMBER { $$ = $1 + $3; } | NUMBER ‘-’ NUMBER { $$ = $1 - $3; } ;

Coordinating flex and bison n Example to return int value: [0 -9]+ { yylval Coordinating flex and bison n Example to return int value: [0 -9]+ { yylval = atoi(yytext); return NUMBER; } sets value for use in actions This one just returns the numeric value of the string stored in yytext returns recognized token atoi is C function to convert string to integer In prior flex examples we just returned tokens, not values n Also need to skip whitespace, return symbols [ t] ; /* ignore white space */ n return 0; /* logical EOF */. return yytext[0];

Bison Rule Details n n n Unlike flex, bison doesn’t care about line boundaries, Bison Rule Details n n n Unlike flex, bison doesn’t care about line boundaries, so add white space for readability Symbol on lhs of first rule is start symbol, can override with %start declaration in definition section Symbols in bison have values, must be “declared” as some type ¨ YYSTYPE determines type ¨ Default for all values is int ¨ We’ll be using different types for YYSTYPE in the Simple. Calc exercises

Bison Parser – Definition Section n Definition Section ¨ Tokens used in grammar should Bison Parser – Definition Section n Definition Section ¨ Tokens used in grammar should be defined. Example rule: n expression: NUMBER ‘+’ NUMBER { $$ = $1 + $3; } The token NUMBER should be defined. Later we’ll see cases where expression should also be defined, and how to define tokens with other data types. %token must be lowercase, e. g. , : n %token NUMBER n ¨ From the tokens that are defined, Bison will create an appropriate header file ¨ Single quoted characters can be used as tokens without declaring them, e. g. , ‘+’, ‘=‘ etc.

Lex - Definition Section Must include the header created by bison n Must declare Lex - Definition Section Must include the header created by bison n Must declare yylval as extern n %{ #include "simple. Calc. tab. h extern int yylval; #include %}

Bison Parser – C Section n At a minimum, provide yyerror and main routines Bison Parser – C Section n At a minimum, provide yyerror and main routines yyerror(char *errmsg) { fprintf(stderr, "%sn", errmsg); } main() { yyparse(); }

Bison Intro Exercise n n Download Simple. Calc. y, Simple. Calc. l and mbison. Bison Intro Exercise n n Download Simple. Calc. y, Simple. Calc. l and mbison. bat Create calculator executable ¨ n FYI, mbison includes these steps: ¨ ¨ ¨ n mbison simple. Calc bison -d simple. Calc. y flex -L -osimple. Calc. c simple. Calc. l gcc -c simple. Calc. c gcc -c simple. Calc. tab. c gcc -Lc: progra~1gnuwin 32lib simple. Calc. o simple. Calc. tab. o -osimple. Calc. exe -lfl –ly Test with valid sentences (e. g. , 3+6 -4) and invalid sentences.

Understanding simple. Calc %{ #include Understanding simple. Calc %{ #include "simple. Calc. tab. h" extern int yylval; %} %% [0 -9]+ { yylval = atoi(yytext); return NUMBER; } [ t] ; /* ignore white space */ n return 0; /* logical EOF */. return yytext[0]; %% /*--------------------*/ /* 5. Other C code that we need. */ yyerror(char *errmsg) { fprintf(stderr, "%sn", errmsg); } main() { yyparse(); } simple. Calc. l Explanation: When the lexer recognizes a number [0 -9]+ it returns the token NUMBER and sets yylval to the corresponding integer value. When the lexer sees a carriage return it returns 0. If it sees a space or tab it ignores it. When it sees any other character it returns that character (the first character in the yytext buffer). If the yyparse recognizes it – good! Otherwise the parser can generate an error. #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { NAME = 258, NUMBER = 259 }; #endif /* Tokens. */ #define NAME 258 simple. Calc. tab. h #define NUMBER 259

Understanding simple. Calc, continued %token NAME NUMBER %% statement: NAME '=' expression | expression Understanding simple. Calc, continued %token NAME NUMBER %% statement: NAME '=' expression | expression { printf("= %dn", $1); } ; expression: expression '+' NUMBER { $$ = $1 + $3; } | expression '-' NUMBER { $$ = $1 - $3; } | NUMBER { $$ = $1; } ; Explanation Execute simple. Calc and enter expression 1+2 main program calls yyparse. This calls lex to recognize 1 as a NUMBER (puts 1 in yylval), sets $$ = $1 Calls lex which returns +, matches ‘+’ in first expression rhs Calls lex to recognize 2 as a NUMBER (puts 2 in yylval) Recognize expression + NUMBER and “reduce” this rule, does action {$$ = $1 + $3}. Recognizes expression as a statement, so it does the printf action.

Simple. Calc n Do simple. Calc exercise #1 Simple. Calc n Do simple. Calc exercise #1

Adding other variable types* n n YYSTYPE determines the data type of the values Adding other variable types* n n YYSTYPE determines the data type of the values returned by the lexer. If lexer returns different types depending on what is read, include a union: %union { char cval; char *sval; int ival; } n n // C feature, allows one memory area to // be interpreted in different ways. // For bison, will be used with yylval The union will be placed at the top of your. y file (in the definitions section) Tokens and non-terminals should be defined using the union * relates to Simple. Calc exercise 2

Adding other variable types - Example n Definitions in simple. Calc. y: %union { Adding other variable types - Example n Definitions in simple. Calc. y: %union { float fval; int ival; } %token NUMBER %token FNUMBER %type expression n Use union in rules in simple. Calc. l: {DIGIT}+ { yylval. ival = atoi(yytext); return NUMBER; }

Processing lexemes in flex* n n Sometimes you want to modify a lexeme before Processing lexemes in flex* n n Sometimes you want to modify a lexeme before it is passed to bison. This can be done by putting a function call in the flex rules Example: to convert input to lower case ¨ put a prototype for your function in the definition section (above first %%) ¨ write the function definition in the C-code section (bottom of file) ¨ call your function when the token is recognized. Use strdup to pass the value to bison. * relates to Simple. Calc exercise 3

Example continued %{ #include “example. tab. h“ need prototype here void make_lower(char *text_in); %} Example continued %{ #include “example. tab. h“ need prototype here void make_lower(char *text_in); %} %% function call to process text [a-z. A-Z]+ {make_lower(yytext); yylval. sval = strdup(yytext); make duplicate using strdup return token type return KEYWORD; } %% void make_lower(char *text_in) { function code in C section int i; for (i=0; i

Adding actions to rules * For more complex processing, functions can be added to Adding actions to rules * For more complex processing, functions can be added to bison. n Remember to add a prototype at the top, and the function at the bottom n * relates to Simple. Calc exercise 4

Processing more than one line * To process more than one line, ensure the Processing more than one line * To process more than one line, ensure the n is simply ignored n Use a recursive rule to allow multiple inputs n * relates to Simple. Calc exercise 4

Example todo. l %{ #include Example todo. l %{ #include "todo. tab. h" %} %% groceries | clothes | dinner | book | bicycle | home { yylval. sval = strdup(yytext); return NOUN; } [ tnr]+ ; /* ignore white space */ [a-z. A-Z]+ { yylval. sval = strdup(yytext); return NAME; }. { return yytext[0]; } <> return 0; buy | write | %% int yyerror(char *errmsg) { read | fprintf(stderr, "error is: %sn", errmsg); ride | } eat | main() { yyparse(); } go { yylval. sval = strdup(yytext); return VERB; } All one file – two columns used to fit one slide.

Example todo. y // Recognizes Example todo. y // Recognizes "to-do" list in form name { verb noun } %union { %% char *sval; void end(char* name) { } printf("That is all for now, %s!n", name); %{ exit(0); void end(char*); }; %} %token VERB NOUN NAME %type todo. List person task entry %% entry: person '{' todo. List '}' { end($1); } ; person: NAME { printf("Greetings, %sn", $1); $$ = $1; } ; todo. List: todo. List task | task ; task: VERB NOUN { printf("You need to %s %sn", $1, $2); } ; All one file – two columns used to fit one slide.

Simple. Calc n Do remaining simple. Calc exercises Simple. Calc n Do remaining simple. Calc exercises

Using files with Bison The standard file for Bison is yyin. The following code Using files with Bison The standard file for Bison is yyin. The following code can be used to take an optional command-line argument: int main(argc, argv) int argc; char **argv; { FILE *file; if (argc == 2) { file = fopen(argv[1], "r"); if (!file) { fprintf(stderr, “Couldn't open %sn", argv[1]); exit(1); } yyin = file; } n

More details (if you’re curious) Running flex creates simple. Calc. c. This creates the More details (if you’re curious) Running flex creates simple. Calc. c. This creates the following case statement (I added the printf statements: case 1: YY_RULE_SETUP printf("returning number value %dn", atoi(yytext)); { yylval = atoi(yytext); return NUMBER; } YY_BREAK case 2: YY_RULE_SETUP printf("ignoring white spacen"); ; /* ignore white space */ YY_BREAK case 3: YY_RULE_SETUP printf("recognized eofn"); return 0; /* logical EOF */ YY_BREAK case 4: YY_RULE_SETUP printf("returning other character %cn", yytext[0]); return yytext[0]; YY_BREAK

Continuing more detail Running bison creates simple. Calc. tab. c switch (yyn) { case Continuing more detail Running bison creates simple. Calc. tab. c switch (yyn) { case 3: #line 4 "simple. Calc. y" { printf("= %dn", (yyvsp[0])); ; } break; case 4: #line 7 "simple. Calc. y" { (yyval) = (yyvsp[-2]) + (yyvsp[0]); ; } break; case 5: #line 8 "simple. Calc. y" { (yyval) = (yyvsp[-2]) - (yyvsp[0]); ; } break; case 6: #line 9 "simple. Calc. y" { (yyval) = (yyvsp[0]); ; } break; Notice use of stack pointer sp for $values NOTE: I added extra printf statements to each case, which is what you can see in the trace.

Continuing more detail n In exercise 2 you define a union. This gets translated Continuing more detail n In exercise 2 you define a union. This gets translated to code within Simple. Calc. tab. h: #if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) #line 1 "simple. Calc. Ex 2. y" typedef union YYSTYPE { float fval; int ival; } YYSTYPE; extern YYSTYPE yylval; This is what makes your yylval return part of the union

Continuing more detail n Symbols you define in bison’s CFG are added to a Continuing more detail n Symbols you define in bison’s CFG are added to a symbol table: static const char *const yytname[] = { "$end", "error", "$undefined", "NUMBER", "FNUMBER", "NAME", "'='", "'+'", "'*'", "'('", "')'", "$accept", "statement", "expression", "term", "factor", 0 };

Continuing more detail New rules make use of union: switch (yyn) { case 3: Continuing more detail New rules make use of union: switch (yyn) { case 3: #line 15 "simple. Calc. Ex 2. y" { printf("= %fn", (yyvsp[0]. fval)); ; } break; n case 4: #line 18 "simple. Calc. Ex 2. y" { (yyval. fval) = (yyvsp[-2]. fval) + (yyvsp[0]. fval); ; } break; case 5: #line 19 "simple. Calc. Ex 2. y" { (yyval. fval) = (yyvsp[0]. fval); ; } break; expression is defined as , so is NUMBER

Summary of steps (from online manual) The actual language-design process using Bison, from grammar Summary of steps (from online manual) The actual language-design process using Bison, from grammar specification to a working compiler or interpreter, has these parts: 1. Formally specify the grammar in a form recognized by Bison (i. e. , machine-readable BNF). For each grammatical rule in the language, describe the action that is to be taken when an instance of that rule is recognized. The action is described by a sequence of C statements. 2. Write a lexical analyzer to process input and pass tokens to the parser. 3. Write a controlling function (main) that calls the Bison-produced parser. 4. Write error-reporting routines.