IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Caml Discussion :

Ocamllex, Ocamlyacc, le tout avec plusieurs fichiers


Sujet :

Caml

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 104
    Points : 84
    Points
    84
    Par défaut Ocamllex, Ocamlyacc, le tout avec plusieurs fichiers
    Bonjour,

    Toujours dans mon apprentissage de Ocaml, je bloque cette fois sur l'utilisation de Ocamllex et Ocamlyacc. Je prends comme exercice celui disponible à cette page http://www.enseignement.polytechniqu..._Caml_1/td4_5/.

    Je souhaite diviser mon programme en plusieurs fichiers. Un premier contenant les fonctions de traitement du texte, un deuxième contenant le lexer et un dernier contenant le parseur. Le but étant de lire quelque chose comme ça : (paf | pif) & blabla et de construire un arbre ressemblant à (And (Or (paf, pif)), blabla).

    Voici mes différents fichiers :

    texte.ml :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    (* Text analysis *)
     
    type formule =
        Word of string 
      | And of formule * formule
      | Or of formule * formule
      | Minus of formule * formule ;;
     
    type token =
        EOF 
      | LEFT_PARENTHESIS
      | RIGHT_PARENTHESIS
      | SYMBOL_AND
      | SYMBOL_OR
      | SYMBOL_MINUS
      | Identificateur of string ;;
     
    let is_a_letter l = 
      l >= 'a' && l <= 'z' ;;
     
    let cut_words f s =
      let s_length = String.length s in
      let rec cut_word start i =
        if i >= s_length then
          f (String.sub s start (i - start))
        else if is_a_letter (String.get s i) then
          cut_word start (succ i)
        else
          begin
    	f (String.sub s start (i - start));
    	cut i;
          end;
      and cut i =
        if i >= s_length then 
          ()
        else if is_a_letter (String.get s i) then
          cut_word i (succ i)
        else
          cut (succ i)
      in
        cut 0 ;;
     
    let start_with prefix s =
      let prefix_length = String.length prefix in
      let s_length = String.length s in
        if prefix_length > s_length then
          false
        else 
          prefix = (String.sub s 0 prefix_length) ;;
     
    module SceneSet = Set.Make 
      (struct
         type t = int * int
         let compare (a1, s1) (a2, s2) =
           if a1 < a2 then
    	 -1
           else if a1 > a2 then
    	 1
           else
    	 s1 - s2
       end) ;;
     
    type table = (string, SceneSet.t) Hashtbl.t ;;
     
    let add_word (table : table) (value : int * int) (word : string) =
      let entry =
        try
          SceneSet.add value (Hashtbl.find table word)
        with
    	Not_found -> SceneSet.singleton value in
        Hashtbl.replace table word entry ;;
     
    let words_from_table (table : table) = 
      Hashtbl.fold (fun key value init -> key :: init) table [] ;;
     
    let index_file file_name =
      let file = open_in file_name in
      let table = Hashtbl.create 4096 in
      let acts = ref 0 in
      let scenes = ref 0 in
      let treat_file () =
        try 
          while true do
    	let line = input_line file in
    	  if start_with "ACTE" line then
    	    begin
    	      scenes := 0;
    	      acts := !acts + 1;
    	    end
    	  else if start_with "Scene" line then 
    	      scenes := !scenes + 1
    	  else if start_with "FIN" line then
    	    raise End_of_file
    	  else
    	    cut_words (add_word table (!acts, !scenes)) (String.lowercase line)
          done;
          failwith "I should not be there"
        with
    	End_of_file ->
    	  begin
    	    close_in file;
    	    table;
    	  end;
      in
        treat_file () ;;
     
    let get_locations (table : table) word =
      Hashtbl.find table word ;;
     
    let print_locations locations =
      SceneSet.iter 
        (fun (act, scene) -> Printf.printf "(%d,%d)" act scene)
        locations ;;
     
    let rec get_locations_with_formule (table : table) formule =
      match formule with
          Word (w) -> 
    	get_locations table w
        | And (f1, f2) -> 
    	SceneSet.inter 
    	  (get_locations_with_formule table f1)
    	  (get_locations_with_formule table f2)
        | Or (f1, f2) ->
    	SceneSet.union 
    	  (get_locations_with_formule table f1)
    	  (get_locations_with_formule table f2)
        | Minus (f1, f2) ->
    	SceneSet.diff 
    	  (get_locations_with_formule table f1)
    	  (get_locations_with_formule table f2) ;;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    (* File lexer.ml *)
     
    {
      open Parser
      exception Eof
    }
     
    rule token = parse
        [' ' '\t'] { }
        | ['\n'] { EOL }
        | ['a'-'z'] as word { WORD (word) }
        | '&' { AND }
        | '|' { OR }
        | '\\' { MINUS }
        | '(' { LEFT_PARENTHESIS }
        | ')' { RIGHT_PARENTHESIS }
        | eof { raise Eof }
     
    {
     
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    /* File parser.ml */
     
    %token <string> WORD
    %token OR AND MINUS
    %token LEFT_PARENTHESIS RIGHT_PARENTHESIS
    %token EOL
     
    %left MINUS
    %left OR
    %left AND
     
    %start main
    %type <(Texte.formule)> main
    %%
     
    main:
      formule EOL { $1 }
    ;
     
    formule:
       WORD { (Word ($1)) }
     | LEFT_PARENTHESIS formule RIGHT_PARENTHESIS { $2 }
     | formule AND formule { (And ($1, $3)) }
     | formule OR formule { (Or ($1, $3)) }
     | formule MINUS formule { (Minus ($1, $3)) }
    ;
    Déjà je ne me suis pas encore bien familiarisé avec la compilation d'un projet Ocaml composé de plusieurs fichiers. Ici, le problème que j'ai est la déclaration du type formule dans le fichier texte.ml et son utilisation dans le module yacc. Au niveau du %type, il me dit "unbound type". J'ai essayé un Texte.formule, ça n'a pas l'air d'améliorer la situation.

    Le programme n'est évidemment pas complet, il manque l'interaction entre l'arbre renvoyé par yacc et la fonction get_locations_with_formule.

    Si vous pouviez m'aiguiller ou me tourner vers des ressources expliquant tout ça clairement, je vous en remercie.

    --
    sperca

  2. #2
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 104
    Points : 84
    Points
    84
    Par défaut
    J'ai fini par trouver à l'aide de différentes documentations.

    En fait, il me manquait pas mal de connaissances sur mli, cmi, cmo etc.

    Le problème est donc résolu. Je mettrai le fonctionnel ici demain.

    Merci quand même.

    Bye

    --
    sperca

  3. #3
    Membre éprouvé
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    309
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2010
    Messages : 309
    Points : 928
    Points
    928
    Par défaut
    Salut

    Bon, je n'ai pas regardé tes problèmes en détail. Mais remarque générale : il est fortement recommandé d'utiliser menhir à la place d'ocamlyacc. C'est beaucoup plus bonheur Et quand tu as plein de fichiers, pour compiler l'ensemble, il faut utiliser ocamlbuild. Surtout sur des projets "simple", ça compile l'ensemble en une commande. Et il y a un support pour menhir dans ocamlbuild.

    Cheers

  4. #4
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 104
    Points : 84
    Points
    84
    Par défaut
    Bonjour,

    Voici donc ma solution à l'exercice et la méthode pour compiler le tout juste en-dessous :

    lexer.mll :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    (* File lexer.ml *)
     
    {
      open Parser
      exception Eof
    }
     
    rule token = parse
        [' ' '\t'] { token lexbuf }
        | ['\n'] { EOL }
        | ['a'-'z']+ as word { WORD (word) }
        | '&' { AND }
        | '|' { OR }
        | '\\' { MINUS }
        | '(' { LEFT_PARENTHESIS }
        | ')' { RIGHT_PARENTHESIS }
        | eof { raise Eof }
     
    {
     
    }
    parser.mly :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    /* File parser.ml */
     
    %{
      open Texte
    %}
     
    %token <string> WORD
    %token OR AND MINUS
    %token LEFT_PARENTHESIS RIGHT_PARENTHESIS
    %token EOL
     
    %left MINUS
    %left OR
    %left AND
     
    %start main
    %type <formule> main
    %%
     
    main:
      formule EOL { $1 }
    ;
     
    formule:
       WORD { (Word ($1)) }
     | LEFT_PARENTHESIS formule RIGHT_PARENTHESIS { $2 }
     | formule AND formule { (And ($1, $3)) }
     | formule OR formule { (Or ($1, $3)) }
     | formule MINUS formule { (Minus ($1, $3)) }
    ;
    texte.ml :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    (* Text analysis *)
     
    type formule =
        Word of string 
      | And of formule * formule
      | Or of formule * formule
      | Minus of formule * formule ;;
     
    let is_a_letter l = 
      l >= 'a' && l <= 'z' ;;
     
    let cut_words f s =
      let s_length = String.length s in
      let rec cut_word start i =
        if i >= s_length then
          f (String.sub s start (i - start))
        else if is_a_letter (String.get s i) then
          cut_word start (succ i)
        else
          begin
    	f (String.sub s start (i - start));
    	cut i;
          end;
      and cut i =
        if i >= s_length then 
          ()
        else if is_a_letter (String.get s i) then
          cut_word i (succ i)
        else
          cut (succ i)
      in
        cut 0 ;;
     
    let start_with prefix s =
      let prefix_length = String.length prefix in
      let s_length = String.length s in
        if prefix_length > s_length then
          false
        else 
          prefix = (String.sub s 0 prefix_length) ;;
     
    module SceneSet = Set.Make 
      (struct
         type t = int * int
         let compare (a1, s1) (a2, s2) =
           if a1 < a2 then
    	 -1
           else if a1 > a2 then
    	 1
           else
    	 s1 - s2
       end) ;;
     
    type table = (string, SceneSet.t) Hashtbl.t ;;
     
    let add_word (table : table) (value : int * int) (word : string) =
      let entry =
        try
          SceneSet.add value (Hashtbl.find table word)
        with
    	Not_found -> SceneSet.singleton value in
        Hashtbl.replace table word entry ;;
     
    let words_from_table (table : table) = 
      Hashtbl.fold (fun key value init -> key :: init) table [] ;;
     
    let index_file file_name =
      let file = open_in file_name in
      let table = Hashtbl.create 4096 in
      let acts = ref 0 in
      let scenes = ref 0 in
      let treat_file () =
        try 
          while true do
    	let line = input_line file in
    	  if start_with "ACTE" line then
    	    begin
    	      scenes := 0;
    	      acts := !acts + 1;
    	    end
    	  else if start_with "Scene" line then 
    	      scenes := !scenes + 1
    	  else if start_with "FIN" line then
    	    raise End_of_file
    	  else
    	    cut_words (add_word table (!acts, !scenes)) (String.lowercase line)
          done;
          failwith "I should not be there"
        with
    	End_of_file ->
    	  begin
    	    close_in file;
    	    table;
    	  end;
      in
        treat_file () ;;
     
    let get_locations (table : table) word =
        Hashtbl.find table word 
     
    let print_locations locations =
      SceneSet.iter 
        (fun (act, scene) -> Printf.printf "(acte : %d, scene : %d)\n" act scene)
        locations ;;
     
    let rec get_locations_with_formule (table : table) formule =
      match formule with
          Word (w) -> 
    	get_locations table w
        | And (f1, f2) -> 
    	SceneSet.inter 
    	  (get_locations_with_formule table f1)
    	  (get_locations_with_formule table f2)
        | Or (f1, f2) ->
    	SceneSet.union 
    	  (get_locations_with_formule table f1)
    	  (get_locations_with_formule table f2)
        | Minus (f1, f2) ->
    	SceneSet.diff 
    	  (get_locations_with_formule table f1)
    	  (get_locations_with_formule table f2) ;;
    motor.ml :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    let main () =
        let lexbuf = Lexing.from_channel stdin in
        let user_formule = Parser.main Lexer.token lexbuf in
        let table = Texte.index_file "don_juan.txt" in
        let locations = Texte.get_locations_with_formule table user_formule in
          Texte.print_locations locations ;;
     
    try
      begin
        print_string "Requete : ";
        flush stdout;
        main ();
      end;
    with 
        Not_found -> Printf.printf "0 result.\n" ;;
    J'ai l'impression que le traitement de l'union ne fonctionne pas, j'ai sûrement loupé un détail, je regarderai ça.

    Voici la méthode pour compiler :

    ocamlc -i texte.ml > texte.mli
    ocamlc -c texte.mli
    ocamlc -c texte.ml
    ocamlyacc parser.mly
    ocamlc -i parser.ml > parser.mli
    ocamlc -c parser.mli
    ocamlc -c parser.ml
    ocamllex lexer.mll
    ocamlc -c lexer.ml
    ocamlc -c motor.ml
    ocamlc -o motor texte.cmo parser.cmo lexer.cmo motor.cmo


    TropMdr > Ta réponse est arrivée pendant que j'écrivais ce message. Je regarderai Mehnir plus tard, merci pour ce conseil. Le soucis de OcamlBuild c'est que je souhaite savoir comment ça fonctionne en détail avant d'automatiser le tout. Mais je l'utiliserai une fois que je me sentirai suffisamment à l'aise.

    Merci ;-)

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Upload d'un repertoire avec plusieurs fichiers.xls
    Par skillipo dans le forum Documents
    Réponses: 1
    Dernier message: 18/03/2008, 10h59
  2. Makefile avec plusieurs fichiers output
    Par virtuadrack dans le forum Systèmes de compilation
    Réponses: 4
    Dernier message: 29/10/2007, 22h48
  3. [Compiler] Compiler une application avec plusieurs fichiers m
    Par ploukinet dans le forum MATLAB
    Réponses: 3
    Dernier message: 10/05/2007, 17h34
  4. Debugger C avec plusieurs fichier *.o
    Par bulki dans le forum C
    Réponses: 3
    Dernier message: 04/05/2007, 11h57
  5. [linux][gcc] Comment travaille t-on avec plusieurs fichiers?
    Par kaygee dans le forum Autres éditeurs
    Réponses: 2
    Dernier message: 02/04/2004, 17h48

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo