Скористаємося можливостями конструкції switch мови програмування С. Як елемент управління скінченого автомата візьмемо поточний стан скінченого автомата (на початку програми q0=0).
int automat (FILE*fp)
{ int с, q=0; // початковий стан автомата
while((c=fgetch(fp))!=EOF)
{ if (index_litera(c) = = -1) break;
switch(q)
{case 0:
if (c>='1' && c<='9") {q=1; break;}
if (c= =' ' || c= ='\n' || c= ='\t' || c= ='\r') break;
if (c= ='0') { q=6; break;}
// помилка – недопустимий початок лексеми
my_error(); return 0;
case 1:
if (c>='0' && c<='9') break;
// в заключному стані
return 1;
case 6:
if (c= ='Х' || c= ='x') {q=12; break;}
if (c>='0' && c<='7') {q=7; break;}
// в заключному стані
return 1;
case 12:
if ((c>='0' && c<='9') ||
(c>='A' && c<='F') ||
(c>='a' && c<='f')) {q=13; break;}
// помилка – невірне продовження шістнадцяткової константи
my_error (); return 0;
// --------------------------------------------------------------------
// в такому стилі програмуємо далі
// --------------------------------------------------------------------
}// кінець конструкції switch
// перевіримо на заключний стан
if (is_final (row)) printf (" Лексема вірна - %s", text);
else printf (''Лексема помилкова-%s", text);
}// кінець циклу зчитування літер
}// кінець функції automat
Наведений вище приклад функції automat, в якій в основу управління покладено поточний стан скінченого автомата, спробуємо певною мірою оптимізувати. З тексту модуля видно, що основні операції – це операції порівняння та перевірка належності поточної вхідної літери діапазону літер. При цьому враховується конкретна властивість кодової таблиці ASCI: коди латинських літер та цифр займають певні діапазони. Щоб функція automat не залежала від властивостей кодової таблиці, запропонуємо новий варіант її реалізації.
Розділимо літери кодової таблиці на класи:
- проміжок, \n, \v, \r, \t, \b – літери, які розділяють лексеми;
- 1, 2, 3,..7 – вісімкові цифри (крім 0);
- 0,...9,A,..F,a,..f — шістнадцяткові цифри;
- 0 – вісімковий або десятковий нуль
- клас, до якого входять літери ASCI: \, .
Як бачимо, в загальному випадку класи можуть перетинатися.
Розглянемо таблицю, об'ємом 256 елементів, в якій буде відмічатися належність літер до окремих класів.
# include <stdio.h>
#define DECIMAL 0x01
#define OCTAL 0x02
#define EMPTY 0x80
#define LITERA 0x04
char decimal[]=’0123456789’;
char hesh_digit[] = ‘0123456789ABCDEFabcdef’;
char octal[] = ‘01234567’;
char empty[] = ‘ \n\r\t\v\r\b’;
char litera[]=
‘ABCDEFGHIJKLMNOPQRSTUVUXYZ_abcdefghijklmnopqrstuvuxyz’
char vector_upr [256];
// початкова ініціалізація
void set_class (char * str, int code)
{ int i, len = strlen(str);
for (i=0; i<len; i + +) vector_upr [(int)*(str+i)] |= code;
}
int automat (FILE * fp)
{ int c, q =0; // початковий стан автомата
while ((c = fgetch(fp))! = EOF)
{ class_litera = vector_upr [(int) (c)];
if (!class_litera) break; // літера не належить множині sigma
switch (q)
{ case 0: if (class_litera & DECIMAL) { q=1; break;}
if (c == ‘0’) {q=6; break; }
if (class_litera & EMPTY) break;
return ERROR;
// в такому стилі програмуємо далі
… …. … …..
}
} }
main () {
int i;
FILE*fp; char file_name[80];
REPEAT:
printf("Вкажіть ім'я файла з лексеми:");
if(scant("%s", file_name) == 0) return 0; //відмовляється працювати
if(fp = fopen (file_name,"rt") == NULL)
{ printf ("файл %s не відкрито.\ n"); goto REPEAT;}
for (i = 0; i < 256; i++) *(vector_upr + i) = 0;
set_class(decimal, DECIMAL);
set_class(octal, OCTAL); set class(litera, LITERA);
set class(empty, EMPTY);
while (! eof (fp))
if (automat(fp) == ERROR) printf (" Лексема вірна - %s", text);
else printf (''Лексема помилкова-%s", text);
}// кінець main