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 } { }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.
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)) } ;
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
Partager