Open In App

Introduction to YACC

Improve
Improve
Like Article
Like
Save
Share
Report

Each translation rule input to YACC has a string specification that resembles a production of a grammar-it has a nonterminal on the LHS and a few alternatives on the RHS. For simplicity, we will refer to a string specification as a production. YACC generates an LALR(1) parser for language L from the productions, which is a bottom-up parser. The parser would operate as follows: For a shift action, it would invoke the scanner to obtain the next token and continue the parse by using that token. While performing a reduced action in accordance with production, it would perform the semantic action associated with that production. 

The semantic actions associated with productions achieve the building of an intermediate representation or target code as follows: 

  • Every nonterminal symbol in the parser has an attribute.
  •  The semantic action associated with a production can access attributes of nonterminal symbols used in that production–a symbol “$n’ in the semantic action, where n is an integer, designates the attribute of the nonterminal symbol in the RHS of the production and the symbol ‘$$’ designates the attribute of the LHS nonterminal symbol of the production. 
  • The semantic action uses the values of these attributes for building the intermediate representation or target code. 

A parser generator is a program that takes as input a specification of a syntax and produces as output a procedure for recognizing that language. Historically, they are also called compiler compilers. YACC (yet another compiler-compiler) is an LALR(1) (LookAhead, Left-to-right, Rightmost derivation producer with 1 lookahead token) parser generator. YACC was originally designed for being complemented by Lex. 

Input File: YACC input file is divided into three parts.

/* definitions */
 ....

%% 
/* rules */ 
....
%% 

/* auxiliary routines */
.... 

Input File: Definition Part:

  • The definition part includes information about the tokens used in the syntax definition:
%token NUMBER 
%token ID 
  • Yacc automatically assigns numbers for tokens, but it can be overridden by
%token NUMBER 621 
  • Yacc also recognizes single characters as tokens. Therefore, assigned token numbers should no overlap ASCII codes.
  • The definition part can include C code external to the definition of the parser and variable declarations, within %{ and %} in the first column.
  • It can also include the specification of the starting symbol in the grammar:
%start nonterminal 

Input File: Rule Part:

  • The rules part contains grammar definitions in a modified BNF form.
  • Actions is C code in { } and can be embedded inside (Translation schemes).

Input File: Auxiliary Routines Part:

  • The auxiliary routines part is only C code.
  • It includes function definitions for every function needed in the rules part.
  • It can also contain the main() function definition if the parser is going to be run as a program.
  • The main() function must call the function yyparse().

Input File:

  • If yylex() is not defined in the auxiliary routines sections, then it should be included:
#include "lex.yy.c"  
  • YACC input file generally finishes with:
 .y 

Output Files:

  • The output of YACC is a file named y.tab.c
  • If it contains the main() definition, it must be compiled to be executable.
  • Otherwise, the code can be an external function definition for the function int yyparse()
  • If called with the –d option in the command line, Yacc produces as output a header file y.tab.h with all its specific definition (particularly important are token definitions to be included, for example, in a Lex input file).
  • If called with the –v option, Yacc produces as output a file y.output containing a textual description of the LALR(1) parsing table used by the parser. This is useful for tracking down how the parser solves conflicts.

Example: Yacc File (.y) 

C




%{
   #include <ctype.h>
   #include <stdio.h>
   #define YYSTYPE double /* double type for yacc stack */
%}
 
%%
 Lines :  Lines S '\n' { printf("OK \n"); }
       |  S '\n’
       |  error '\n' {yyerror("Error: reenter last line:");
                        yyerrok; };
 S     :  '(' S ')’
       '[' S ']’
       |   /* empty */    ;
%%
 
#include "lex.yy.c"
  
void yyerror(char * s)
/* yacc error handler */
{  
 fprintf (stderr, "%s\n", s);
}
  
int main(void)
 {
 return yyparse();
 


Lex File (.l) 

C




%{
%}
 
%%
[ \t]     { /* skip blanks and tabs */ }
\n|.      { return yytext[0]; }
%%


For Compiling YACC Program:

  1. Write lex program in a file file.l and yacc in a file file.y
  2. Open Terminal and Navigate to the Directory where you have saved the files.
  3. type lex file.l
  4. type yacc file.y
  5. type cc lex.yy.c y.tab.h -ll
  6. type ./a.out


Last Updated : 14 Mar, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads