Ciao Giuseppe,
Flex e Bison sono due strumenti molto potenti(si, in Flex puoi gestire tutte le casistiche che vuoi) ma, per poterli sfruttare al massimo è necessario studiare la teoria degli automi.
Un buon testo è il seguente:
John E. Hopcroft, Rajeev Motwani, Jeffrey D. Ullman - Automi, linguaggi e calcolabilità - ADDISON-WESLEY.
Nel tuo caso è più semplice implementare a mano un automa per il riconoscimento dei letterali stringa. Questo dovrebbe esserti di aiuto:
codice:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <tchar.h>
#define MAX_STRING 2048
typedef enum tagStati
{
S_ERROR = -1, S0 = 0, S1, S2, S3, S4, S5, S6, S7, S8
} Stati;
typedef struct tagLista
{
int NumRigaIniziale;
int NumRigaFinale;
char *Stringa;
struct tagLista* next;
} Lista;
Lista* NewNode(int NumRigaIniziale, int NumRigaFinale, const char *Stringa)
{
int len;
Lista *n;
n = (Lista *)malloc(sizeof(Lista));
if( n == NULL )
return NULL;
len = strlen(Stringa);
n->Stringa = (char*)malloc(len*sizeof(char) + 1);
if ( n->Stringa == NULL )
{
free(n);
return NULL;
}
n->NumRigaIniziale = NumRigaIniziale;
n->NumRigaFinale = NumRigaFinale;
strcpy(n->Stringa, Stringa);
n->next = NULL;
return n;
}
Lista* Append(Lista* first, int NumRigaIniziale, int NumRigaFinale, const char *Stringa)
{
Lista *n = first, *nuovo;
// catena vuota
if ( first == NULL )
return NewNode(NumRigaIniziale, NumRigaFinale, Stringa);
n = first;
while( n->next != NULL )
{
n = n->next;
}
nuovo = NewNode(NumRigaIniziale, NumRigaFinale, Stringa);
n->next = nuovo;
return first;
}
void Free(Lista* first)
{
Lista *n1 = first, *n2;
while ( n1 != NULL )
{
n2 = n1->next;
free(n1->Stringa);
free(n1);
n1 = n2;
}
}
void Print(Lista *first)
{
Lista *n = first;
while( n != NULL )
{
printf("Riga: %d - Stringa: \"%s\"\n", n->NumRigaIniziale, n->Stringa);
n = n->next;
}
}
Stati Automa(const char* szNomeFile, Lista** pLista)
{
int NumLinea = 1;
int NumPrimaLinea = 1;
Stati stato = S0;
int z = 0;
FILE *fp;
char str[MAX_STRING];
char c;
/* Apriamo il file per la lettura */
fp = fopen(szNomeFile, "r");
if ( !fp )
{
printf("Errore nell'apertura del file\n");
return 0;
}
while ( fread((void*)&c, 1, 1, fp) == 1 )
{
if ( c == '\n' )
NumLinea++;
switch ( stato )
{
case S0:
if ( c == '"' )
{
stato = S1;
NumPrimaLinea = NumLinea;
}
else if ( c == '/' )
{
stato = S4;
}
break;
case S1:
if ( c == '"' )
{
stato = S2;
}
else if ( c == '\\' )
{
stato = S3;
}
else
{
str[z++] = c;
}
break;
case S2:
if ( c == ' ' || c == '\t' || c == '\n' ||
c == '_' || c == 'T' || c == '(' || c == ')' )
;
else if ( c == '"' )
{
stato = S1;
}
else
{
str[z] = '\0';
z = 0;
*pLista = Append(*pLista, NumPrimaLinea, NumLinea, str);
if ( pLista == NULL )
{
printf("Errore nell'allocazione di memoria!\n");
return S_ERROR;
}
str[z] = '\0';
stato = S0;
}
break;
case S3:
if ( c != '\\' && c != '\n' && c != '"' )
{
str[z++] = c;
}
else if ( c == '"' )
{
str[z] = '\0';
z = 0;
*pLista = Append(*pLista, NumPrimaLinea, NumLinea, str);
if ( pLista == NULL )
{
printf("Errore nell'allocazione di memoria!\n");
return S_ERROR;
}
str[z] = '\0';
stato = S0;
}
break;
case S4:
if ( c == '*' )
{
stato = S5;
}
else if ( c == '/' )
{
stato = S7;
}
else
{
stato = S0;
}
break;
case S5:
if ( c == '*' )
{
stato = S6;
}
break;
case S6:
if ( c == '/' )
{
stato = S0;
}
break;
case S7:
if ( c == '\n' )
{
stato = S0;
}
break;
default:
stato = S_ERROR;
break;
}
}
fclose(fp);
fp = NULL;
return stato;
}
int main()
{
Stati stato;
Lista * pLista = NULL;
stato = Automa("Prova.cpp", &pLista);
printf("\n");
if ( stato == S0 )
Print(pLista);
else
printf("Errore!");
Free(pLista);
return 0;
}
La funzione Automa accetta come parametri il nome di un file sorgente e un puntatore a una lista concatenata che serve a memorizzare le stringhe. La lista memorizza anche il numero di linea.
Per esempio, se il file "prova.cpp" contiene il seguente codice:
codice:
#include <iostream>
using namespace std;
/*
Questo è un "commento"
su più righe
*/
// Questo è un "commento", su riga singola.
int main(int argc, char *argv[])
{
char *pProva1 = "Questa e' una\
stringa su due linee";
char *pProva2 = "Questa e' un'altra "
"stringa su due linee";
char *pProva3 = _T( "Questa e' la terza\
stringa su due linee" );
char *pProva4 = _T( "Questa e' la quarta "
"stringa su due linee" );
char *pProva5 = _T( "Questa e' la quinta " )
_T( "stringa su due linee" );
cout << "Prova 1: " << pProva1 << endl;
cout << "Prova 2: " << pProva1 << endl;
cout << "Prova 3: " << pProva1 << endl;
cout << "Prova 4: " << pProva1 << endl;
cout << "Prova 5: " << pProva1 << endl;
return 0;
}
L'output del programma è il seguente: