Fleks (brzi generator leksičkog analizatora) je slobodna verzija Lex-а. Obično se koristi sa slobodnim Bison generatorom. Flex nastao oko 1987. godine i prvobitno je napisan u C-u, a njegov autor je Vern Paxson.

Fleks
Programer(i)Vern Paxson
Repozitorijum Uredi na Vikipodacima
TipGenerator leksičkog analizatora
LicencaBSD license
Veb-sajtflex.sf.net

Opis flex-a dat je u uputstvu za njegovo korišćenje:

"Flex je programska alatka za generisanje skenera (čitača): programa koji prepoznaju leksičke šablone u tekstu. Flex čita datu ulaznu datoteku ili standardni ulaz, ukoliko ime ulazne datoteke nije zadato, radi opisa čitača koji će da generiše. Opisi su u obliku regularnih izraza i C koda, i zovu se pravila. Flex generiše kao izlaz С datoteku, 'lex.yy.c', koja poziva funkciju 'yylex()'. Ova С datoteka je kompajlirana i povezana (linkovana) sa '-lfl' datotekom da bi bilo moguće izvršavanje programa. Kada se pokrene program, on analizira dati ulaz tražeći onaj deo ulaza koji odgovara datom regularnom izrazu. Kada nađe taj deo ulaza, onda se izvršava odgovarajući deo C koda..."

Leksički čitač koji odgovara C++ je flex++, i on je deo flex paketa. Flex++ je dostupan na juniks sistemima bazirani na besplatnoj GNU licenci. On je takođe dostupan i nejuniksovskim sistemima.

Flex zapravo služi za čitanje znakova i pravljenje odgovarajućih tokena koristeći deterministički konačni automat (DKA). DKA je teoretska mašina koja prihvata regularne izraze.

Primer leksičkog analizatora

uredi

Ovo je primer čitača (napisanog u C-u) za programski jezik PL/0.

Simboli koji se prepoznaju su: '+', '-', '*', '/', '=', '(', ')', ',', ';', '.', ':=', '<', '<=', '<>', '>', '>='; brojevi: 0-9 {0-9}; identifikatori: a-zA-Z {a-zA-Z0-9} i ključne reči: "begin", "call", "const", "do", "end", "if", "odd", "procedure", "then", "var", "while".

Spoljašnje promenljive koje se koriste:

FILE *source /* изворна датотека */
int cur_line, cur_col, err_line, err_col /* за извештавање о грешци */
int num /* овде се уписује последњи прочитани број */
char id[] /* Овде се уписује последњи прочитани идентификатор */
Hashtab *keywords /* листа кључних речи */

Spoljašnje funkcije koje se pozivaju:

error(const char msg[]) /* Извештава о грешци */
Hashtab *create_htab(int estimate) /* Креира претраживачку табелу */
int enter_htab(Hashtab *ht, char name[], void *data) /* Додаје променљиву претраживачкој табели */
Entry *find_htab(Hashtab *ht, char *s) /* Проналази променљиву у претраживачкој табели */
void *get_htab_data(Entry *entry) /* Враћа податак из претраживачке табеле */
FILE *fopen(char fn[], char mode[]) /* Отвара датотеку за читање */
fgetc(FILE *stream) /* Чита следећи знак из тока */
ungetc(int ch, FILE *stream) /* Враћа натраг прочитани знак у ток */
isdigit(int ch), isalpha(int ch), isalnum(int ch) /* Класификација знакова */

Spoljašnji tipovi:

Symbol /* Набројиви тип свих симбола у PL/0 језику */
Hashtab /* Репрезентује претраживачку табелу */
Entry /* Репрезентује променљиву у претраживачкој табели */

Čitač se pokreće pozivanjem init_scan, prosleđujući ime ulazne datoteke. Ako je ulazna datoteka uspešno otvorena, ponovo se poziva getsym i vraća uspešno pročitan simbol iz ulazne datoteke.

Srce čitača, getsym, bi trebalo da bude jednostavno. Prvo se preskaču beline. Onda se pronađeni simboli klasifikuju. Ako simbol predstavlja više znakova, onda je potrebno izvršiti dodatne operacije. Brojevi se konvertuju zadatu formu, a za identifikatore se proverava da li odgovaraju nekoj ključnoj reči.

int read_ch(void) {
  int ch = fgetc(source);
  cur_col++;
  if (ch == '\n') {
    cur_line++;
    cur_col = 0;
  }
  return ch;
}

void put_back(int ch) {
  ungetc(ch, source);
  cur_col--;
  if (ch == '\n') cur_line--;
}

Symbol getsym(void) {
  int ch;

  while ((ch = read_ch()) != EOF && ch <= ' ')
    ;
  err_line = cur_line;
  err_col  = cur_col;
  switch (ch) {
    case EOF: return eof;
    case '+': return plus;
    case '-': return minus;
    case '*': return puta;
    case '/': return podeljeno;
    case '=': return jednako;
    case '(': return ozagrada;
    case ')': return zzagrada;
    case ',': return zarez;
    case ';': return tacka_zarez;
    case '.': return tacka;
    case ':':
      ch = read_ch();
      return (ch == '=') ? becomes : nul;
    case '<':
      ch = read_ch();
      if (ch == '>') return nije_jednako;
      if (ch == '=') return manje_jednako;
      put_back(ch);
      return manje;
    case '>':
      ch = read_ch();
      if (ch == '=') return vece_jednako;
      put_back(ch);
      return vece;
    default:
      if (isdigit(ch)) {
        num = 0;
        do {  /* ne proverava se prekoracenje! */
          num = 10 * num + ch - '0';
          ch = read_ch();
        } while ( ch != EOF && isdigit(ch));
        put_back(ch);
        return broj;
      }
      if (isalpha(ch)) {
        Entry *entry;
        id_len = 0;
        do {
          if (id_len < MAX_ID) {
            id[id_len] = (char)ch;
            id_len++;
          }
          ch = read_ch();
        } while ( ch != EOF && isalnum(ch));
        id[id_len] = '\0';
        put_back(ch);
        entry = find_htab(keywords, id);
        return entry ? (Symbol)get_htab_data(entry) : ident;
      }

      error("getsym: neodgovarajuci karakter '%c'", ch);
      return nul;
  }
}

int init_scan(const char fn[]) {
  if ((source = fopen(fn, "r")) == NULL) return 0;
  cur_line = 1;
  cur_col = 0;
  keywords = create_htab(11);
  enter_htab(keywords, "begin", beginsym);
  enter_htab(keywords, "call", callsym);
  enter_htab(keywords, "const", constsym);
  enter_htab(keywords, "do", dosym);
  enter_htab(keywords, "end", endsym);
  enter_htab(keywords, "if", ifsym);
  enter_htab(keywords, "odd", oddsym);
  enter_htab(keywords, "procedure", procsym);
  enter_htab(keywords, "then", thensym);
  enter_htab(keywords, "var", varsym);
  enter_htab(keywords, "while", whilesym);
  return 1;
}

Odgovarajući kod na flex-u za generisanje čitača za isti jezik:

%{
#include "y.tab.h"
%}

cifra [0-9]
slovo [a-zA-Z]

%%
"+" { return PLUS; }
"-" { return MINUS; }
"*" { return PUTA; }
"/" { return PODELJENO; }
"(" { return OZAGRADA; }
")" { return ZZAGRADA; }
";" { return TACKA_ZAREZ; }
"," { return ZAREZ; }
"." { return TACKA; }
":=" { return JE; }
"=" { return JEDNAKO; }
"<>" { return NIJE_JEDNAKO; }
"<" { return MANJE; }
">" { return VECE; }
"<=" { return MANJE_JEDNAKO; }
">=" { return VECE_JEDNAKO; }
"begin" { return BEGINSYM; }
"call" { return CALLSYM; }
"const" { return CONSTSYM; }
"do" { return DOSYM; }
"end" { return ENDSYM; }
"if" { return IFSYM; }
"odd" { return ODDSYM; }
"procedure" { return PROCSYM; }
"then" { return THENSYM; }
"var" { return VARSYM; }
"while" { return WHILESYM; }
{letter}({letter}|{digit})* {
                       yylval.id = (char *)strdup(yytext);
                       return IDENTIFIKATOR;      }
{digit}+ { yylval.num = atoi(yytext);
                       return BROJ;     }
[ \t\n\r] /* preskoci beline */
. { printf("Unknown character [%c]\n",yytext[0]);
                       return NEPOZNATO;    }
%%

int yywrap(void){return 1;}

50 linija koda na flex-u predstavlja oko 100 linija ručno pisanog koda.


Pogledajte još

uredi