Master STL - M1
Module CA (MI190)
Caompilation Anvancée
Université Pierre et Marie Curie
Année 2006–2007

TD-TME 1 - séance du 13 février 2007



Analyses lexicales

Exercices 1 et 2 : expressions régulières - des outils

un exemple :
# let french_date_of d =
  match d with
   [mm; jj; yy] -> jj^"/"^mm^"/"^yy
  | _ -> failwith "Bad date format" ;;
val french_date_of : string list -> string = <fun>

# let english_date_format = Str.regexp "[0-9]+\.[0-9]+\.[0-9]+" ;;
val english_date_format : Str.regexp = <abstr>

# let trans_date l = 
  try
   let i=Str.search_forward english_date_format l 0 in
   let d1 = Str.matched_string l in
   let d2 = french_date_of (Str.split (Str.regexp "\.") d1) in
     Str.global_replace english_date_format d2 l
  with Not_found -> l ;;
val trans_date : string -> string = <fun>

# trans_date "..............06.13.99............" ;;
- : string = "..............13/06/99............"

Exercice 3 : outils(ocamllex/lex/flex)

Un fichier d'entrée de ocamllex se compose de :
 {
   entête  -  expression caml  -  même chose qu'en ocamlyacc
 }

 rule    point-d'entrée   =
   parse  expression-régulière  {  action  }
    | ...
    | expression-régulière  {  action  }
 and    point-d'entrée   =
   parse ...
 and ...
 ;;
Ici, les caractères doivent être compris entre deux quotes (par exemple 's') et une chaîne entre deux double-quotes (par exemple "toto"). Ce qui donne par exemple : ['a' - 'z'] pour dire tous les caractères entre le a minuscule et le z minuscule.

Exercice 4 : outils (Genlex)

le type token :
type token =
    Kwd of string
  | Ident of string
  | Int of int
  | Float of float
  | String of string
  | Char of char ;;
la fonction de création :
Genlex.make_lexer ;;
- : string list -> char Stream.t -> Genlex.token Stream.t = <fun>
un exemple : création des mots clés
let keywords =
  [ "REM"; "GOTO"; "LET"; "PRINT"; "INPUT"; "IF"; "THEN";
    "-"; "!"; "+"; "-"; "*"; "/"; "%"; 
    "="; "<"; ">"; "<="; ">="; "<>";
    "&"; "|" ] ;;
fonction d'analyse lexicale :
# let line_lexer l = Genlex.make_lexer keywords (Stream.of_string l) ;;
val line_lexer : string -> Genlex.token Stream.t = <fun>
# line_lexer "LET x = x + y * 3" ;;
- : Genlex.token Stream.t = <abstr>
Manipulation des flots :
# let rec spaces s =
   match s with parser
     [<'' '  ; rest  >] -> spaces rest
   | [<''\t' ; rest  >] -> spaces rest
   | [<''\n' ; rest  >] -> spaces rest
   | [<>] -> ();;
val spaces : char Stream.t -> unit = <fun>
# let rec lexer s =
   spaces s;
   match s with parser
     [< ''('  >] -> [< 'Lsymbol "(" ; lexer s >]
   | [< '')'  >] -> [< 'Lsymbol ")" ; lexer s >]
   | [< ''+'  >] -> [< 'Lsymbol "+" ; lexer s >]
   | [< ''-'  >] -> [< 'Lsymbol "-" ; lexer s >]
   | [< ''*'  >] -> [< 'Lsymbol "*" ; lexer s >]
   | [< ''/'  >] -> [< 'Lsymbol "/" ; lexer s >]
   | [< ''0'..'9' as c; 
        i,v = lexint (Char.code c - Char.code('0')) >] 
       -> [<'Lint i ; lexer v>]
 and lexint r s =
   match s with parser  
     [< ''0'..'9' as c >] 
     -> let u = (Char.code c) - (Char.code '0') in  lexint (10*r + u) s
   | [<>] -> r,s
 ;;
val lexer : char Stream.t -> lexeme Stream.t = <fun>
val lexint : int -> char Stream.t -> int * char Stream.t = <fun>

Exercice 5 : suppression de commentaires

Les commentaires en Objective CAML sont hiérarchiques. On peut ainsi commenter des parties de texte, y compris celles contenant déjà des commentaires. Un commentaire commence par les caractères (* et se termine par *). Voici un exemple :

(* début du commentaire
      sur plusieurs 
         lignes *)

 let succ x = (* fonction successeur *)
   x + 1;;

(* niveau 1  texte commenté 
  let old_succ y = (* niveau 2 fonction successeur niveau 2 *)
    y +1;;
    niveau 1 *)
  succ 2;;


Le but de cet exercice est de créer un nouveau texte sans tous les commentaires. Le choix de l'outil d'analyse lexicale est libre.

Analyse syntaxique descendante

Exercice 6 : mini-Basic

On reprend la description du mini-Basic du projet de L2 de l'an passsé.

Spécifications BASIC_L2 niveau 1

BASIC_L2 niveau 1 comprendra les commandes suivantes :

Réécriture de la grammaire des expressions

Ecrire une grammaire pour une analyse descendante prédictive :

Implantation

Implantez un analyseur pour ce mini-BASIC en utilisant les flots et le module Genlex.

Analyses syntaxiques ascendantes

Exercice 7 : outils (camlyacc/yacc/bison)

Un fichier d'entrée de ocamlyacc se compose de 4 parties :
 %{
   (1) entête  -  expression caml
 %}

   (2) déclaration

 %%
   (3) règlei  actioni
 %%

   (4) queue  -  expression caml

Pour (3), on pourrait avoir
e : O e e  {printf "%s " $1};

e : Constante  {printf "%u " $1}
  | Identificateur {printf "%s " $1};
A partir du fichier d'entrée, Camlyacc génère un fichier Caml-light. On trouve textuellement en tête de ce dernier la partie (1) et en queue la partie (4). On trouve en général dans (1) les overtures de modules (#open) qui sont utilisées par les actions sémantiques contenues dans (3).

Pour (2), les lexèmes déclarés avec Par exemple :

On y trouve aussi la déclaration de l'axiome avec Par exemple :
%start e
%type <unit> e
documentation O'Caml :
        /* File parser.mly */
        %token <int> INT
        %token PLUS MINUS TIMES DIV
        %token LPAREN RPAREN
        %token EOL
        %left PLUS MINUS        /* lowest precedence */
        %left TIMES DIV         /* medium precedence */
        %nonassoc UMINUS        /* highest precedence */
        %start main             /* the entry point */
        %type <int> main
        %%
        main:
            expr EOL                { $1 }
        ;
        expr:
            INT                     { $1 }
          | LPAREN expr RPAREN      { $2 }
          | expr PLUS expr          { $1 + $3 }
          | expr MINUS expr         { $1 - $3 }
          | expr TIMES expr         { $1 * $3 }
          | expr DIV expr           { $1 / $3 }
          | MINUS expr %prec UMINUS { - $2 }
        ;
le fichier camllex :
        (* File lexer.mll *)
        {
        open Parser        (* The type token is defined in parser.mli *)
        exception Eof
        }
        rule token = parse
            [' ' '\t']     { token lexbuf }     (* skip blanks *)
          | ['\n' ]        { EOL }
          | ['0'-'9']+ as lxm { INT(int_of_string lxm) }
          | '+'            { PLUS }
          | '-'            { MINUS }
          | '*'            { TIMES }
          | '/'            { DIV }
          | '('            { LPAREN }
          | ')'            { RPAREN }
          | eof            { raise Eof }
le programme principal :
        (* File calc.ml *)
        let _ =
          try
            let lexbuf = Lexing.from_channel stdin in
            while true do
              let result = Parser.main Lexer.token lexbuf in
                print_int result; print_newline(); flush stdout
            done
          with Lexer.Eof ->
            exit 0
la compilation :
        ocamllex lexer.mll       # generates lexer.ml
        ocamlyacc parser.mly     # generates parser.ml and parser.mli
        ocamlc -c parser.mli
        ocamlc -c lexer.ml
        ocamlc -c parser.ml
        ocamlc -c calc.ml
        ocamlc -o calc lexer.cmo parser.cmo calc.cmo

Exercice 9 : instructions imbriquées (IF THEN ELSE)

Traiter à la Pascal les instructions IF-THEN et IF-THEN-ELSE avec Par exemple : IF T THEN IF T THEN A ELSE A

XMLeries

Exercice 10 : étude de l'analyseur XML-light

Pour les projets nécessitant de faire une analyse syntaxique de langages à balises, il est possible d'utiliser xmllight développé en O'Caml. Les sources sont à téléchargées à l'URL suivante : http://tech.motion-twin.com/xmllight.

En utilisant les fonctions d'analyse syntaxique Xml.parse_string et Xml.parse_file construisez des valeurs XML que vous afficherez avec les fonctions Xml.to_string et Xml.to_string_fmt.


Ce document a été traduit de LATEX par HEVEA