Järgnevas on Bison-i abil realiseeritud mäluga kalkulaator - kasutaja võib omistada muutujatele väärtusi ja neid siis järgmistes operatsioonides kasutada. Muutujad ja funktsioonid salvestatakse nimede tabelis, mis on realiseeritud linkidega nimistuna; kogu see andmestruktuur on kirjeldatud header-failis tabel.h:
/* lingitud symbolite andmetyyp */
struct nimi
{
char *name; /* muutuja nimi */ int type; /* symboli tyyp: kas IDENT või FNCT
*/ union {
double var; /* IDENT väärtus - reaalarv */ double (*fnctptr)(); /* FNCT väärtus */
} value; struct nimi
*next; /* link */ };
typedef struct nimi nimi; /* et poleks tarvis kasutada 'struct' */
/* Nimede tabel: ahel `struct nimi' -st */
extern nimi *sym_table;
nimi *putsym ();
nimi *getsym ();
Bison-i sisendfailis lisatakse see andmestruktuuri kirjeldus #include-failina.
Kalkulaatori sisendis võivad esineda kolme tüüpi lekseemid: muutujad (lekseem IDENT), arvud (lekseem NUM) ja funktsioonide tähistused ( lekseemid tüübiga FNCT), millega on seotud semantiline väärtus. Väärtuse tüüp on lekseemidel IDENT, NUM - double (see on ka mitteterminali exp tüüp) ja lekseemil FNCT - viit selle funktsiooni kirjeldusele. Erineva tüübiga lekseemide ja mitteterminalide kasutamisel tuleb defineerida Bison-i programmi algul tüüpide ühend union ja siis määrata, mis tüüpi mingi lekseem või mitteterminal on:
%union{
double val;
nimi *tptr; }
%token <val> NUM
%token <tptr> IDENT FNCT
%type <val> exp
Sisendis esinevatel ülejäänud lekseemidel ( aritmeetilised operatsioonid, sulud ja omistamismärk =) semantilist väärtust ja tüüpi ei ole.
Erinevate tüüpide tõttu moodustab Bison lekseemi infot sisaldava muutuja yylval kirjeldamiseks andmetüübi YYSTYPE:
typedef union YYSTYPE {
double val;
nimi *tptr; } YYSTYPE;
extern YYSTYPE yylval;
Et YYSTYPE ja yylval kirjeldus oleks ka (eraldi kompileeritavas) Flex-i poolt moodustatud skanneris kättesaadav, tuleb lasta Bisonil need kirjeldused käsuga %defines eraldi h-faili kirjutada ja siis #include -lausega Flex-i sisendprogrammi lisada. Kui Bison-i sisendprogramm on (näiteks) failis calc.y, annab Bison kirjeldusi sisaldava h-faili nimeks calc.tab.h
Omistamisi võib korraga (samal real) teha mittu s.t. kalkulaatorile võib sisestada a = b = 1.3, sellepärast on ka võrdusmärk = defineeritud aritmeetilise operaatorina. Kuna eespooltoodud reas väärtuste omistamised peavad toimuma paremalt vasakule (s.t. operatsioonide sooritamist sulgude abil määrates tuleks eelnevat omistamiste rida tõlgendada nii: a = (b = 1.3)), peab võrdusmärgi assotsiatiivsus olema määratud parempoolseks. Järgnevas on Bison-i sisendfail calc.y:
/*mäluga kalkulaator */
%{
#include <math.h> /* Matemaatiliste funktsioonide cos(), sin(),
jne. kasutamiseks*/
#include "tabel.h" /* Andmestruktuuri `nimi' kirjeldus */
#include <stdio.h>
#include "lex.yy.c"
int i;
nimi *nextid; /* kasutatakse testimisel nimede tabeli trykkimiseks */
%}
%defines
%union {
double val; /* arvud */
nimi *tptr; /* viidad nimede tabelisse */
}
%token <val> NUM /* arvud */
%token <tptr> IDENT FNCT /* identifikaatorid ja funktsioonid */
%type <val> exp
%right '=' /* omistamised toimuvad paremalt vasakule! */
%left '-' '+'
%left '*' '/'
%left NEG /* unaarne miinus */
%right '^' /* astendamine */
/* algab grammatika */
%%
input: /* empty */
| input line
;
line:
'\n'
| exp '\n' { printf ("%d: \t%.10g\n",yylineno-1,
$1); /* reavahetus on juba olnud! */ }
| error '\n' { yyerrok; }
;
exp: NUM { $$ = $1; }
| IDENT { $$ = $1->value.var; }
| IDENT '=' exp { $$ = $3; $1->value.var =
$3; }
| FNCT '(' exp ')' { $$ =
(*($1->value.fnctptr))($3); }
| exp '+' exp { $$ = $1 + $3; } | exp '-' exp { $$ = $1 - $3; }
| exp '*' exp { $$ = $1 * $3; }
| exp '/' exp { $$ = $1 / $3; }
| '-' exp %prec NEG { $$ = -$2; }
| exp '^' exp { $$ = pow ($1, $3); }
| '(' exp ')' { $$ = $2; }
;
%%
main ()
{
init_table (); /* funktsioonide sin, exp jne
tabelisse lisamine - need loeb ka lekseem IDENT */ yyparse ();
/* trykime (testimiseks nimede tabli; muutujate puhul ka muutuja
väärtuse */
extern nimi *sym_table;
nextid = sym_table;
i=1; /* reanumber */
do {
printf( "%d: %s",i, nextid->name );
if (nextid->type == IDENT)
printf("\t: %f",nextid->value.var);
printf("\n");
i++;
nextid = nextid->next;
} while ( nextid->next != NULL );
printf( "%d: %s\n",i, nextid->name );
}
yyerror (s) /* yyparse käivitab vea korral */
char *s;
{
printf ("%s\n", s);
}
Flex-i sisendfailis on C-deklaratsioonide osas #include lausete järel kirjeldatud symboltabel sym_table kui struktuuride struct nimi lingitud nimistu, seejärel kantud sinna viidad funktsioonidele sin, cos jne ja kirjeldatud funkstioonid putsym tabelisse identifikaatori lisamiseks ja getsym - identifikaatori väärtuse saamiseks tablist:
%option noyywrap
%option yylineno
%{
#include "calc.tab.h"
#include <ctype.h>
#include <math.h>
#include <stdio.h>
YYSTYPE yylval;
struct init
{
char *fname;
double (*fnct)(); };
struct init arith_fncts[] /* loetelu kasutatavatest funktsioonidest */
= {
"sin", sin,
"cos", cos, "atan", atan, "ln", log, "exp", exp,
"sqrt", sqrt, 0, 0
};
/* Nimede tabel - struktuuride `struct nimi' ahel */
nimi *sym_table = (nimi *)0;
init_table () /* ka aritmeetilised funktsioonid pannakse tabelisse */
{
int i; nimi *ptr;
for (i = 0; arith_fncts[i].fname != 0; i++)
{ ptr = putsym
(arith_fncts[i].fname, FNCT);
ptr->value.fnctptr = arith_fncts[i].fnct; } }
nimi * putsym (char *sym_name, int sym_type)
{
nimi *ptr; ptr =
(nimi *) malloc (sizeof (nimi));
ptr->name = (char *) malloc (strlen (sym_name) + 1); strcpy (ptr->name,sym_name); ptr->type = sym_type;
ptr->value.var = 0; /* algväärtustamine - ka fctn jaoks */
ptr->next = (struct nimi *)sym_table;
sym_table = ptr;
return ptr; }
nimi * getsym (char *sym_name)
{
nimi *ptr;
for (ptr = sym_table; ptr != (nimi *) 0; ptr = (nimi *)ptr->next)
if (strcmp (ptr->name,sym_name) == 0)
return ptr;
return 0; }
%}
Lekseemide kirjeldus on üsna tavaline; veidi "nipitama" peab vaid arvude ja identifikaatorite puhul:
DIGIT [0-9]
ID [A-Za-z][A-Za-z0-9]*
TYHIK [ \t]+
%%
"+" return ('+');
"-" return ('-');
"*" return ('*');
"/" return ('/');
"^" return ('^');
"=" return('=');
"(" return('(');
")" return(')');
"\n" return('\n');
{DIGIT}+("."{DIGIT}*)? {yylval.val=atof(yytext);return(NUM);}
"."{DIGIT}+ {yylval.val=atof(yytext);return(NUM);}
{ID} {nimi *s;s = getsym (yytext);
if (s == 0)
s = putsym (yytext, IDENT);
yylval.tptr = s;
return s->type;};
{TYHIK} ; /* tyhikud ja tabulatsiooonimärgid kustutatakse */
%%
Testime sisendfailiga:
5+2*4
pi = 3.141592653589
sin(pi)
a = b = 1.3
a + 2*b
- ln(a)
c = exp(ln(b))
b = 2*b
1: 13
2: 3.141592654
3: 7.932657894e-013
4: 1.3
5: 3.9
6: -0.2623642645
7: 1.3
8: 2.6
1: c : 1.300000
2: b : 2.600000
3: a : 1.300000
4: pi : 3.141593
5: sqrt
6: exp
7: ln
8: atan
9: cos
10: sin