C/C++ Programming Pearls

 Flex/Bison 
Sample1: Simple infix calculator using flex and bison with optional input from a string or file. See download
 
 
 main.c 
/**
 * Example code for flex/yacc
 * Makefile and part of main.c taken from Kyle R. Burton <mortis@voicenet.com>
 * and http://www.gnu.org/manual/bison-1.35/bison.html and modified.
 * 
 * 
 * Author: Mike Chirico <mchirico@users.sourceforge.net>
 *  
 * Copyright 2003  Mike Chirico, All rights reserved.
 * This source code is copyrighted under the GNU GPL.
 *
 * Input can be read 1 of 2 ways.  From a file ./main file or
 * if no file is given, it will read from buff and buff2.
 *
 * Note, to show how parser.y handles errors, buff2 is incorrect.
 */
#include <stdio.h>



/**
 * We have to declare these here - they're not  in any header files 
 * we can inclde.  yyparse() is declared with an empty argument list  
 * so that it is compatible with the generated C code from bison.
 *
 */
extern int yyparse();
extern FILE *yyin;
extern int yy_scan_string(const char *);
extern void reset_lexer(void);
extern void reset_parser(void);


int main(int argc,char** argv)
{




  /**
   * Two strings the parser will work on.
   * The second string, by design, contains
   * 2 errors.
   */
  char *buff =  "3.2+5.3\n"
                "5.0e2+5.2E2\n"
                "(7+9)*2\n"
                "r=34+2\n"
		"r\n";
  

  char *buff2 = "3+3\n"
                "5+-2\n"
		"2^(5--2)\n"
                "5/2\n"
                "5/0\n"
                "5/0.001\n"
                "5+3\n"
                "+3+3";



  /**
   * take input from file only,then, exit
   */
    if ( argc == 2 )
      {
            yyin = fopen( argv[1], "r" );
	    reset_lexer();
	    reset_parser();
	    yyparse();
	    return(1);
	    /*Exit program  from here */
	  }



  printf("main: scaning buf:\n%s\nOutput:",buff);
  reset_lexer();                      /* implemented in lexer.l */
  reset_parser();                     /* implemented in parser.y */
  /**
   *  command to set input.
   */
  yy_scan_string(buff);               
  /**
   * invoke the parser
   */
  yyparse();


  /**
   * scan the second string
   */
  printf("main: scaning buf2 (Note 2 errors in buf2 by design):\n%s\nOutput:",buff2);
  reset_lexer();
  reset_parser();
  yy_scan_string(buff2);
  yyparse();

  return 0;
}


 
 
 
 lexer.l 
%{
#include "parser.h"
extern double sngvbltable[26];
int line = 1, col = 1;


%}

%%
([0-9]+|([0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?)   { col += (int) strlen(yytext); yylval.dval = atof(yytext); return NUM; } 
[ \t]   { col += (int) strlen(yytext); }               /* ignore but count white space */
[a-z]   { col += (int) strlen(yytext); yylval.vblno = yytext[0] - 'a'; return NAME; }
\n      { col = 0; ++line; return yytext[0]; }
.       { col += (int) strlen(yytext); return yytext[0]; }
%%
/**
 * reset the line and column count
 *
 *
 */
void reset_lexer(void)
{

  line = 1;
  col  = 1;

}

/**
 * yyerror() is invoked when the lexer or the parser encounter
 * an error. The error message is passed via *s
 * 
 *
 */
void yyerror(char *s)
{
  printf("error: %s at line: %d col: %d\n",s,line,col);

}

/**
 * Normaly, the scanner reads from file handles, this function
 * is responsible for telling the scanner that there is no more
 * input.  It should return true if there are no more input files,
 * or if there are, it should close the previous file, open the next,
 * and return false (0).
 *
 *
 */
int yywrap(void)
{
  return 1;
}


 
 
 
 
 parser.y 
%{
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
double sngvbltable[26]; /* single variable lookup table */
extern int yyerror(char *s);
extern int yylex( void );
extern char* yytext;

int num = 0;
%}

%union {
  double dval;
  int vblno;
}
%token <vblno> NAME
%token <dval> NUM
%left '-' '+'
%left '*' '/'  /* order defines precedence '+' and '-' are at lowest level */
%left NEG     /* negation--unary minus */
%right '^'    /* exponentiation        */
%type <dval> exp


/* Grammar follows */
%%
 input:    /* empty string */
        | statement '\n'
	| input statement '\n'
;

 statement:    NAME '=' exp {sngvbltable[$1] = $3; }
        | exp   { printf ("\t%g\n", $1); }
;
/* Precedence for '+','-' vs '*',  '/' is NOT defined here.
 * Instead, the two %left commands above perform that role.
 */
 exp:  exp '+' exp        { $$ = $1 + $3;    }
        | exp '-' exp        { $$ = $1 - $3;    }
        | exp '*' exp        { $$ = $1 * $3;    }
        | exp '/' exp        { if ( $3 == 0.0)
                                 yyerror("divide by zero error");
                               else 
	                          $$ = $1 / $3;    }
        | '-' exp  %prec NEG { $$ = -$2;        }
        | exp '^' exp        { $$ =  pow ($1, $3); }
        | '(' exp ')'        { $$ = $2;         }
        | NUM
	| NAME		     { $$ = sngvbltable[$1]; }
;
%%

 
 
 
 Makefile 
main: main.o lexer.o parser.o
	gcc -o main -Wall main.c lexer.o parser.c -lm

parser.o: parser.c

parser.c: parser.y
	bison -d parser.y
	test -e parser.tab.c && mv parser.tab.c parser.c
	test -e parser.tab.h && mv parser.tab.h parser.h
parser.h: parser.c

lexer.o: lexer.c

lexer.c: lexer.l parser.h
	flex lexer.l
	test -e lex.yy.c && mv lex.yy.c lexer.c


clean:
	-rm -f main *.o parser.h parser.c lexer.c a.out *.a core

test: main
	./main

SourceForge.net Logo