Laboratorium 2: generatory analizatorów leksykalnych

Poznajemy generator analizatorów leksykalnych dla naszego języka programowania. Może to obejmować Flex (C/C++), Alex (Haskell), ewentualnie JFlex (Java) i inne generatory.

W programie napisanym na poprzednim laboratorium, analizator leksykalny zamieniamy na wygenerowany przy pomocy odpowiedniego generatora.

Flex

Omawiamy przykład analizatora stworzonego przy uzyciu Flexa, np.

%{
// #include "exp.tab.h" /* Definiuje leksemy, np. NUM */
#define NUM '9'
int yylval;
%}
%option noyywrap
%%
[0-9]+  {
         yylval = atoi( yytext ) ;
         return (int)NUM;
       }
.  { return (int)(yytext[0]); }
\n  { return (int)'\n'; }
%%
int main() { int lexem; while(lexem=yylex()) { ... } 
 return 0;
}

Uruchamianie:
flex -o lexer.c lexer.l

Wygenerowany plik (tu: lexer.c) zawiera funkcję yylex(), której kolejne wywołania dają kolejne leksemy (0 dla EOF jeśli nie ma innych wytycznych)

JFlex

Omawiamy przykład, np.

%%
%class Scanner
%type Symbol
%unicode
%line 
 
%{
  StringBuffer string = new StringBuffer();
  // Leksemy są klasy Symbol, zdefiniowanej poza tym plikiem
  private Symbol symbol(int type) {
    return new Symbol(type, yyline, -1);
  }
  private Symbol symbol(int type, Object value) {
    return new Symbol(type, yyline, -1, value);
  }
%}
 
WhiteSpace     = (LineTerminator | [ \t\f])
DecIntLiteral = 0 | [1-9][0-9]*
 
%%
(0|[1-9][0-9]*) 	{ return symbol(sym.NUM, new Integer(yytext())); }
{WhiteSpace}		{ }
.		{ System.out.println("Unexpected character:"+yytext());}

Z tej specyfikacji JFlex wygeneruje klasę (tu: Scanner) zawierającą kod leksera. Konstruktor tej klasy bierze jako argument klasy java.io.Reader reprezentujący strumień wejściowy. Kolejne wywołania metody yylex() z tej klasy dają kolejne leksemy (null dla EOF w tym wypadku)

Alex

Omawiamy przykład, np.

{
module CalcLex(alexScanTokens) where
import CalcToken(Token(..))
}
%wrapper "basic"
$digit = 0-9                    
tokens :-
  $white+                        ; -- whitespace
  "--".*                            ; -- comment
  $digit+                        {\s -> Int (read s)}
  [\=\+\-\*\/\(\)]               {\s -> Sym (head s)}

Przykład uzycia:

$ alex CalcLex.x
$ ghci CalcLex.hs
GHCi, version 6.12.1...
Ok, modules loaded: CalcLex, CalcTokens.
*CalcLex> alexScanTokens "let x = 1 in x +x"
[Let,Var "x",Sym '=',Int 1,In,Var "x",Sym '+',Var "x"]