In buona sostanza flex viene usato per l'analisi lessicale e si usa spesso insieme a bison che effettua l'analisi sintattica.
Quello che segue è un esempio di un piccolo interprete per un piccolo linguaggio di programmazione:
symtab.h:
codice:
#ifndef SYMTAB_H
#define SYMTAB_H
#define HT_SIZE 251
#define SCOPE_SIZE 4096
typedef enum
{
T_INTEGER,
T_REAL,
T_STRING,
T_BOOLEAN
} enumTipo1;
typedef enum
{
T_CONST,
T_VAR
} enumTipo2;
typedef union
{
int iVal;
double dblVal;
char *strVal;
enum {FALSE, TRUE} bVal;
} Valore;
typedef struct tagHashTable
{
char *Key;
enumTipo1 Tipo1;
enumTipo2 Tipo2;
Valore Value;
char bInizializzato;
struct tagHashTable *next;
} HashTable;
typedef struct tagScope
{
int top;
HashTable **stack[SCOPE_SIZE];
} Scope;
HashTable* htNewNode(char *Key, enumTipo1 t1, enumTipo2 t2, Valore v, char bInizializzato);
int htFind(HashTable **pHashTable, char *Key, HashTable *pDati);
void htInsert(HashTable **pHashTable, char *Key, enumTipo1 t1, enumTipo2 t2, Valore v, char bInizializzato);
void htFree(HashTable* first);
void scopeInit(Scope *pScope);
void scopePush(Scope *pScope);
int scopePop(Scope *pScope);
void scopeFree(Scope *pScope);
int scopeFind(Scope* pScope, char *Key, HashTable *pDati);
int scopeInsert(Scope *pScope, char *Key, enumTipo1 t1, enumTipo2 t2, Valore v, char bInizializzato);
int scopeUpdateValue(Scope *pScope, char *Key, enumTipo1 t1, enumTipo2 t2, Valore v, char bInizializzato);
#endif // SYMTAB_H
symtab.c:
codice:
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include "symtab.h"
HashTable* htNewNode(char *Key, enumTipo1 t1, enumTipo2 t2, Valore v, char bInizializzato)
{
HashTable *n;
n = (HashTable*)malloc(sizeof(HashTable));
if( n == NULL )
return NULL;
n->Key = (char*)malloc(strlen(Key)+1);
if ( n->Key == NULL )
{
free(n);
return NULL;
}
strcpy(n->Key, Key);
n->Tipo1 = t1;
n->Tipo2 = t2;
n->Value = v;
n->bInizializzato = bInizializzato;
n->next = NULL;
if ( !bInizializzato )
{
switch(t1)
{
case T_INTEGER:
n->Value.iVal = 0;
break;
case T_REAL:
n->Value.dblVal = 0.0;
break;
case T_STRING:
//n->Value.strVal = NULL;
n->Value.strVal = (char*)malloc(sizeof(char)*1);
strcpy(n->Value.strVal, "");
break;
case T_BOOLEAN:
n->Value.bVal = FALSE;
break;
}
}
return n;
}
int htFind(HashTable **pHashTable, char *Key, HashTable *pDati)
{
int index = 0;
HashTable *t;
int a = 31415;
int b = 27183;
char *s = Key;
for(; *s != '\0'; s++)
index = (a*index + *s) % HT_SIZE;
if ( index < 0 )
index *= -1;
t = pHashTable[index];
while ( t != NULL )
{
if ( strcmp(t->Key, Key) == 0 )
{
pDati->Key = t->Key;
pDati->Tipo1 = t->Tipo1;
pDati->Tipo2 = t->Tipo2;
pDati->Value = t->Value;
pDati->bInizializzato = t->bInizializzato;
return index;
}
t = t->next;
}
return -1;
}
void htInsert(HashTable **pHashTable, char *Key, enumTipo1 t1, enumTipo2 t2, Valore v, char bInizializzato)
{
int index = 0;
HashTable *t = NULL;
int a = 31415;
int b = 27183;
char *s = Key;
for(; *s != '\0'; s++)
index = (a*index + *s) % HT_SIZE;
if ( index < 0 )
index *= -1;
t = pHashTable[index];
if ( t == NULL )
{
pHashTable[index] = htNewNode(Key, t1, t2, v, bInizializzato);
return;
}
while ( t != NULL )
{
if ( strcmp(t->Key, Key) == 0 )
{
//pHashTable[index]->pLista = ListAppend(pHashTable[index]->pLista, pos);
printf("\nErrore: La chiave %s e' gia' presente nella hashtable\n", Key);
return;
}
if ( t->next == NULL )
{
t->next = htNewNode(Key, t1, t2, v, bInizializzato);
//t = t->next;
//t->next = NULL;
return;
}
t = t->next;
}
}
void htFree(HashTable* first)
{
HashTable *n1 = first, *n2;
while ( n1 != NULL )
{
n2 = n1->next;
if ( n1->Key )
free(n1->Key);
if ( n1->Tipo1 == T_STRING )
{
if ( n1->Value.strVal )
free(n1->Value.strVal);
}
free(n1);
n1 = n2;
}
}
void scopeInit(Scope *pScope)
{
HashTable **pHT;
int x;
pScope->top = 0;
pHT = (HashTable**)malloc(sizeof(HashTable*) * HT_SIZE);
if ( pHT != NULL )
{
for ( x = 0; x < HT_SIZE; x++ )
{
pHT[x] = (HashTable*)malloc(sizeof(HashTable));
if ( pHT[x] == NULL )
{
printf("Memoria non sufficiente.\n");
return;
}
pHT[x] = NULL;
}
}
else
{
printf("Memoria non sufficiente.\n");
return;
}
pScope->stack[pScope->top] = pHT;
for ( x = 1; x < SCOPE_SIZE; x++ )
pScope->stack[x] = NULL;
}
void scopePush(Scope *pScope)
{
HashTable **pHT;
int x;
pScope->top++;
if ( pScope->top > SCOPE_SIZE - 1 )
{
printf("Stack scope pieno!\n");
pScope->top = SCOPE_SIZE - 1;
return;
}
pHT = (HashTable**)malloc(sizeof(HashTable*) * HT_SIZE);
if ( pHT != NULL )
{
for ( x = 0; x < HT_SIZE; x++ )
{
pHT[x] = (HashTable*)malloc(sizeof(HashTable));
if ( pHT[x] == NULL )
{
printf("Memoria non sufficiente.\n");
return;
}
pHT[x] = NULL;
}
}
else
{
printf("Memoria non sufficiente.\n");
return;
}
pScope->stack[pScope->top] = pHT;
}
int scopePop(Scope *pScope)
{
HashTable **pHT;
int x;
if ( pScope->top < 0 )
return pScope->top;
pHT = pScope->stack[pScope->top];
for ( x = 0; x < HT_SIZE; x++)
htFree(pHT[x]);
free(pHT);
pScope->stack[pScope->top] = NULL;
pScope->top--;
return pScope->top;
}
void scopeFree(Scope *pScope)
{
while ( scopePop(pScope) >= 0 )
;
}
int scopeFind(Scope* pScope, char *Key, HashTable *pDati)
{
HashTable **pHT;
int x;
for ( x = pScope->top; x >= 0; x-- )
{
pHT = pScope->stack[x];
if ( htFind(pHT, Key, pDati) >= 0 )
{
return 1;
}
}
return 0;
}
int scopeInsert(Scope *pScope, char *Key, enumTipo1 t1, enumTipo2 t2, Valore v, char bInizializzato)
{
HashTable **pHT;
HashTable Dati;
pHT = pScope->stack[pScope->top];
if ( htFind(pHT, Key, &Dati) >= 0 )
{
printf("Errore: la variabile '%s' e' gia' stata dichiarata nello scope %d\n", Key, pScope->top);
return 0;
}
htInsert(pHT, Key, t1, t2, v, bInizializzato);
return 1;
}
int scopeUpdateValue(Scope *pScope, char *Key, enumTipo1 t1, enumTipo2 t2, Valore v, char bInizializzato)
{
HashTable **pHT;
HashTable Dati;
int x;
int index;
for ( x = pScope->top; x >= 0; x-- )
{
pHT = pScope->stack[x];
index = htFind(pHT, Key, &Dati);
if ( index >= 0 )
{
HashTable *pTable = pHT[index];
while ( strcmp(pTable->Key, Key) != 0 )
pTable = pTable->next;
pTable->Value = v;
pTable->bInizializzato = bInizializzato;
return 1;
}
}
printf("Errore: variabile '%s' non dichiarata\n", Key);
return 0;
}
ast.h:
codice:
#ifndef AST_H
#define AST_H
#include "symtab.h"
typedef enum { typeCon, typeId, typeOpr } nodeEnum;
/* Costanti */
typedef struct
{
Valore value;
} conNodeType;
/* identificatori */
typedef struct
{
char *name;
char *uniqueName;
Valore value;
char bInizializzato;
} idNodeType;
/* operatori */
typedef struct
{
int oper; /* operatori */
int nops; /* numero di operandi */
struct nodeTypeTag *op[1]; /* operandi */
} oprNodeType;
typedef struct nodeTypeTag
{
nodeEnum type;
enumTipo1 type1;
enumTipo2 type2;
union
{
conNodeType con; /* costanti */
idNodeType id; /* identificatori */
oprNodeType opr; /* operatori */
};
} nodeType;
nodeType *opr(int oper, int nops, ...);
nodeType *id(char *name, char *uniqueName, enumTipo1 t1, enumTipo2 t2, Valore value, char bInizializzato);
nodeType *con(enumTipo1 t1, Valore value);
void freeNode(nodeType *p);
#endif // AST_H
ast.c:
codice:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "ast.h"
#define SIZEOF_NODETYPE ((char *)&p->con - (char *)p)
extern void yyerror(char *s);
nodeType *con(enumTipo1 t1, Valore value)
{
nodeType *p = NULL;
size_t nodeSize;
nodeSize = SIZEOF_NODETYPE + sizeof(conNodeType);
if ((p = malloc(nodeSize)) == NULL)
yyerror("out of memory");
p->type = typeCon;
p->type1 = t1;
p->type2 = T_CONST;
p->con.value = value;
return p;
}
nodeType *id(char *name, char *uniqueName, enumTipo1 t1, enumTipo2 t2, Valore value, char bInizializzato)
{
nodeType *p = NULL;
size_t nodeSize;
nodeSize = SIZEOF_NODETYPE + sizeof(idNodeType);
if ((p = malloc(nodeSize)) == NULL)
yyerror("out of memory");
p->type = typeId;
p->type1 = t1;
p->type2 = t2;
p->id.name = (char*)malloc(sizeof(char)*strlen(name)+1);
strcpy(p->id.name, name);
p->id.uniqueName = (char*)malloc(sizeof(char)*strlen(uniqueName)+1);
strcpy(p->id.uniqueName, uniqueName);
p->id.value = value;
p->id.bInizializzato = bInizializzato;
return p;
}
nodeType *opr(int oper, int nops, ...)
{
va_list ap;
nodeType *p = NULL;
size_t nodeSize;
int i;
nodeSize = SIZEOF_NODETYPE + sizeof(oprNodeType) +
(nops - 1) * sizeof(nodeType*);
if ((p = malloc(nodeSize)) == NULL)
yyerror("out of memory");
p->type = typeOpr;
p->opr.oper = oper;
p->opr.nops = nops;
va_start(ap, nops);
for (i = 0; i < nops; i++)
p->opr.op[i] = va_arg(ap, nodeType*);
va_end(ap);
return p;
}
void freeNode(nodeType *p) {
int i;
if (!p) return;
if (p->type == typeOpr) {
for (i = 0; i < p->opr.nops; i++)
freeNode(p->opr.op[i]);
}
free (p);
}