diff --git a/.gitignore b/.gitignore index 150014ac59b6d4819f637ab53476e87d20bb089d..512be27d3196e8ab747a7153009bc0d80be949cf 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ alpaga/_build grammar.html src/generated_parser.ml **/*.native +alpaga/ml_parser_generator alpaga/alpaga alpaga/ml_parser_generator.native tykernel/*.ast diff --git a/Sujet.pdf b/Sujet.pdf index 599245c9d0473d8b57b5eb2e5828f84909c9c45b..9c9948eab943c7c556b18536d7ab8620e30dba72 100644 Binary files a/Sujet.pdf and b/Sujet.pdf differ diff --git a/src/Makefile b/src/Makefile index ca5bcb5fa0ccd11646173548ff62ed30efebd72d..3be2372f51ee4697b1e354d1ff921e7d63177f63 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,17 +1,17 @@ SRC=archi.ml ast.ml builtins.ml config.ml cfg_constprop.ml cfg_dead_assign.ml \ cfg.ml cfg_print.ml cfg_gen.ml cfg_nop_elim.ml cfg_run.ml elang.ml \ -elang_print.ml elang_gen.ml elang_run.ml generated_parser.ml lexer_generator.ml \ -linear_dse.ml linear_liveness.ml linear.ml linear_print.ml linear_gen.ml \ -linear_run.ml ltl.ml ltl_print.ml ltl_gen.ml ltl_run.ml ltl_debug.ml main.ml \ -options.ml parser.ml prog.ml regalloc.ml report.ml riscv.ml rtl.ml rtl_print.ml \ -rtl_gen.ml rtl_run.ml symbols.ml utils.ml +elang_print.ml elang_gen.ml elang_run.ml e_regexp.ml generated_parser.ml \ +lexer_generator.ml linear_dse.ml linear_liveness.ml linear.ml linear_print.ml \ +linear_gen.ml linear_run.ml ltl.ml ltl_print.ml ltl_gen.ml ltl_run.ml \ +ltl_debug.ml main.ml options.ml parser.ml prog.ml regalloc.ml report.ml \ +riscv.ml rtl.ml rtl_print.ml rtl_gen.ml rtl_run.ml symbols.ml utils.ml TG = main.native all: $(TG) $(TG): $(SRC) - ocamlbuild -cflags -warn-error,"+a" -use-ocamlfind $(TG) + ocamlbuild -cflags -warn-error,"+a-26" -use-ocamlfind $(TG) clean: rm -rf _build diff --git a/src/e_regexp.ml b/src/e_regexp.ml new file mode 100644 index 0000000000000000000000000000000000000000..4a43143b98e31754a974dd8f3db3fc1b0850b262 --- /dev/null +++ b/src/e_regexp.ml @@ -0,0 +1,137 @@ +open Batteries +open Symbols +open Utils +(* Expressions régulières *) + +(* Nous modélisons les expressions régulières avec le type suivant. + + Une expressions régulière est soit : + - [Eps] qui dénote l'expressions vide. + - [Charset cs] dénote l'expression régulière qui matche l'ensemble + des caractères de l'ensemble [cs]. + - [Cat(r1,r2)] dénote la concaténation de [r1] et [r2] : reconnaît les mots + [uv] tels que [u] appartient à [r1] et [v] appartient à [r2]. + - [Alt(r1,r2)] dénote un choix entre [r1] et [r2] : reconnaît les mots reconnus + par [r1] et les mots reconnus par [r2]. + - [Star r] dénote la répétition 0, 1 ou plusieurs fois de l'expression [r]. +*) + +type 'a set = 'a Set.t + +type regexp = + | Eps + | Charset of char set + | Cat of regexp * regexp + | Alt of regexp * regexp + | Star of regexp + +(* [char_regexp c] reconnaît le caractère [c] uniquement. *) +let char_regexp c = Charset (Set.singleton c) + +(* [char_range l] reconnaît l'ensemble des caractères de [l]. *) +let char_range (l: char list) = + Charset (Set.of_list l) + +(* [str_regexp s] reconnaît la chaîne de caractère [s]. *) +let str_regexp (s: char list) = + List.fold_right (fun c reg -> Cat(Charset (Set.singleton c), reg)) s Eps + +(* [plus r] reconnaît 1 fois ou plus l'expression [r]. *) +let plus r = Cat(r,Star r) + +(* Fonction d'affichage. Peut être utile pour déboguer. *) +let rec string_of_regexp r = + match r with + Eps -> "Eps" + | Charset c -> Printf.sprintf "[%s]" (string_of_char_list (Set.to_list c)) + | Alt (r1,r2) -> Printf.sprintf "(%s)|(%s)" + (string_of_regexp r1) (string_of_regexp r2) + | Cat (r1,r2) -> Printf.sprintf "(%s)(%s)" + (string_of_regexp r1) (string_of_regexp r2) + | Star r -> Printf.sprintf "(%s)*" (string_of_regexp r) + +(* La liste des expressions régulières permettant d'identifier les tokens du langage E *) +let list_regexp = + let lowercase_letters = "abcdefghijklmnopqrstuvwxyz" in + let uppercase_letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" in + let digits = "0123456789" in + let other_characters = "?!=<>_ ;,{}()[]-+*/%\n\t" in + let alphabet = char_list_of_string (lowercase_letters ^ uppercase_letters ^ digits ^ other_characters) in + let letter_regexp = char_range (char_list_of_string (uppercase_letters ^ lowercase_letters)) in + let digit_regexp = char_range (char_list_of_string digits) in + let keyword_regexp s = str_regexp (char_list_of_string s) in + [ + (keyword_regexp "while", fun s -> Some (SYM_WHILE)); + (keyword_regexp "int", fun s -> Some (SYM_INT)); + (* begin TODO *) + (Eps, fun s -> Some (SYM_VOID)); + (Eps, fun s -> Some (SYM_CHAR)); + (Eps, fun s -> Some (SYM_IF)); + (Eps, fun s -> Some (SYM_ELSE)); + (Eps, fun s -> Some (SYM_RETURN)); + (Eps, fun s -> Some (SYM_PRINT)); + (Eps, fun s -> Some (SYM_STRUCT)); + (Eps, fun s -> Some (SYM_POINT)); + (Eps, fun s -> Some (SYM_PLUS)); + (Eps, fun s -> Some (SYM_MINUS)); + (Eps, fun s -> Some (SYM_ASTERISK)); + (Eps, fun s -> Some (SYM_DIV)); + (Eps, fun s -> Some (SYM_MOD)); + (Eps, fun s -> Some (SYM_LBRACE)); + (Eps, fun s -> Some (SYM_RBRACE)); + (Eps, fun s -> Some (SYM_LBRACKET)); + (Eps, fun s -> Some (SYM_RBRACKET)); + (Eps, fun s -> Some (SYM_LPARENTHESIS)); + (Eps, fun s -> Some (SYM_RPARENTHESIS)); + (Eps, fun s -> Some (SYM_SEMICOLON)); + (Eps, fun s -> Some (SYM_COMMA)); + (Eps, fun s -> Some (SYM_ASSIGN)); + (Eps, fun s -> Some (SYM_EQUALITY)); + (Eps, fun s -> Some (SYM_NOTEQ)); + (Eps, fun s -> Some (SYM_LT)); + (Eps, fun s -> Some (SYM_GT)); + (Eps, fun s -> Some (SYM_LEQ)); + (Eps, fun s -> Some (SYM_GEQ)); + (Eps, fun s -> Some (SYM_IDENTIFIER s)); + (* end TODO *) + (Cat(keyword_regexp "//", + Cat(Star (char_range (List.filter (fun c -> c <> '\n') alphabet)), + Alt (char_regexp '\n', Eps))), + fun s -> None); + (Cat(keyword_regexp "/*", + Cat( + Star (Alt (char_range (List.filter (fun c -> c <> '*') alphabet), + Cat (char_regexp '*', + char_range (List.filter (fun c -> c <> '/') alphabet)))), + keyword_regexp "*/")), + fun s -> None); + (Cat (char_regexp '\'', + Cat (char_range (List.filter (fun c -> c <> '\'' && c <> '\\') alphabet), + char_regexp '\'')), + fun s -> Some (SYM_CHARACTER (String.get s 1))); + (Cat (char_regexp '\'', Cat (char_regexp '\\', + Cat (char_range (char_list_of_string "\\tn0"), + char_regexp '\''))), + fun s -> match String.get s 2 with + | '\\' -> Some (SYM_CHARACTER '\\') + | 'n' -> Some (SYM_CHARACTER '\n') + | 't' -> Some (SYM_CHARACTER '\t') + | '0' -> Some (SYM_CHARACTER '\x00') + | _ -> None + ); + (Cat (char_regexp '"', + Cat (Star ( + Alt ( + char_range (List.filter (fun c -> c <> '"' && c <> '\\') alphabet), + Cat (char_regexp '\\', char_range (char_list_of_string "tn0\\\"")) + ) + ), + char_regexp '"')), + fun s -> Some (SYM_STRING (Stdlib.Scanf.unescaped (String.slice ~first:1 ~last:(-1) s)))); + (char_regexp ' ', fun s -> None); + (char_regexp '\n', fun s -> None); + (char_regexp '\t', fun s -> None); + (plus digit_regexp, fun s -> Some (SYM_INTEGER (int_of_string s))); + (Eps, fun s -> Some (SYM_EOF)) + ] + diff --git a/src/lexer_generator.ml b/src/lexer_generator.ml index def98ae931bc7133fae6037f5411bb5c717fe94c..00311754535022c355d1cce9c2c7aabef5b556c8 100644 --- a/src/lexer_generator.ml +++ b/src/lexer_generator.ml @@ -2,80 +2,10 @@ open Batteries open BatList open Symbols open Utils +open E_regexp type 'a set = 'a Set.t -let remove_dups l : 'a list = - List.fold_left (fun acc elt -> if List.mem elt acc then acc else elt::acc) [] l - -let rec take n l = - if n = 0 then [] - else match l with - | [] -> [] - | a::r -> a::take (n-1) r - -let char_list_of_string l : char list = - String.to_list l - -let string_of_char_list cl = - String.of_list cl - -let string_of_char_set s = - string_of_char_list (Set.to_list s) - -let string_of_int_list l = - Printf.sprintf "%s" (String.concat "_" (List.map string_of_int l)) - -let string_of_int_set s = - string_of_int_list (Set.to_list s) - -(* Expressions régulières *) - -(* Nous modélisons les expressions régulières avec le type suivant. - - Une expressions régulière est soit : - - [Eps] qui dénote l'expressions vide. - - [Charset cs] dénote l'expression régulière qui matche l'ensemble - des caractères de l'ensemble [cs]. - - [Cat(r1,r2)] dénote la concaténation de [r1] et [r2] : reconnaît les mots - [uv] tels que [u] appartient à [r1] et [v] appartient à [r2]. - - [Alt(r1,r2)] dénote un choix entre [r1] et [r2] : reconnaît les mots reconnus - par [r1] et les mots reconnus par [r2]. - - [Star r] dénote la répétition 0, 1 ou plusieurs fois de l'expression [r]. -*) - -type regexp = - | Eps - | Charset of char set - | Cat of regexp * regexp - | Alt of regexp * regexp - | Star of regexp - -(* [char_regexp c] reconnaît le caractère [c] uniquement. *) -let char_regexp c = Charset (Set.singleton c) - -(* [char_range l] reconnaît l'ensemble des caractères de [l]. *) -let char_range (l: char list) = - Charset (Set.of_list l) - -(* [str_regexp s] reconnaît la chaîne de caractère [s]. *) -let str_regexp (s: char list) = - List.fold_right (fun c reg -> Cat(Charset (Set.singleton c), reg)) s Eps - -(* [plus r] reconnaît 1 fois ou plus l'expression [r]. *) -let plus r = Cat(r,Star r) - -(* Fonction d'affichage. Peut être utile pour déboguer. *) -let rec string_of_regexp r = - match r with - Eps -> "Eps" - | Charset c -> Printf.sprintf "[%s]" (string_of_char_list (Set.to_list c)) - | Alt (r1,r2) -> Printf.sprintf "(%s)|(%s)" - (string_of_regexp r1) (string_of_regexp r2) - | Cat (r1,r2) -> Printf.sprintf "(%s)(%s)" - (string_of_regexp r1) (string_of_regexp r2) - | Star r -> Printf.sprintf "(%s)*" (string_of_regexp r) - (* Non-deterministic Finite Automata (NFA) *) (* Les états d'un NFA [nfa_state] sont des entiers. @@ -115,33 +45,40 @@ let empty_nfa = (* Concaténation de NFAs. *) let cat_nfa n1 n2 = + (* TODO *) empty_nfa +(* Alternatives de NFAs *) let alt_nfa n1 n2 = + (* TODO *) empty_nfa +(* Répétition de NFAs *) +(* t est de type [string -> token option] *) let star_nfa n t = + (* TODO *) empty_nfa -(* [nfa_of_regexp r firststate t] construit un NFA qui reconnaît le même langage +(* [nfa_of_regexp r freshstate t] construit un NFA qui reconnaît le même langage que l'expression régulière [r]. - - Pour ce faire, vous allez devoir générer des noms d'états pour vos automates. - Le paramètre [firststate] + [freshstate] correspond à un entier pour lequel il n'y a pas encore d'état dans + le nfa. Il suffit d'incrémenter [freshstate] pour obtenir de nouveaux état non utilisés. + [t] est une fonction du type [string -> token option] utile pour les état finaux. *) -let rec nfa_of_regexp r firststate t = +let rec nfa_of_regexp r freshstate t = match r with - | Eps -> { nfa_states = [firststate]; - nfa_initial = [firststate]; - nfa_final = [(firststate,t)]; - nfa_step = fun q -> []}, firststate + 1 - | Charset c -> { nfa_states = [firststate; firststate + 1]; - nfa_initial = [firststate]; - nfa_final = [firststate + 1, t]; - nfa_step = fun q -> if q = firststate then [(Some c, firststate + 1)] else [] - }, firststate + 2 - | _ -> empty_nfa, firststate + | Eps -> { nfa_states = [freshstate]; + nfa_initial = [freshstate]; + nfa_final = [(freshstate,t)]; + nfa_step = fun q -> []}, freshstate + 1 + | Charset c -> { nfa_states = [freshstate; freshstate + 1]; + nfa_initial = [freshstate]; + nfa_final = [freshstate + 1, t]; + nfa_step = fun q -> if q = freshstate then [(Some c, freshstate + 1)] else [] + }, freshstate + 2 + (* TODO *) + | _ -> empty_nfa, freshstate (* Deterministic Finite Automaton (DFA) *) @@ -182,6 +119,7 @@ let epsilon_closure (n: nfa) (s: nfa_state) : nfa_state set = (* La fonction [traversal visited s] effectue un parcours de l'automate en partant de l'état [s], et en suivant uniquement les epsilon-transitions. *) let rec traversal (visited: nfa_state set) (s: nfa_state) : nfa_state set = + (* TODO *) visited in traversal Set.empty s @@ -189,10 +127,12 @@ let epsilon_closure (n: nfa) (s: nfa_state) : nfa_state set = (* [epsilon_closure_set n ls] calcule l'union des epsilon-fermeture de chacun des états du NFA [n] dans l'ensemble [ls]. *) let epsilon_closure_set (n: nfa) (ls: nfa_state set) : nfa_state set = + (* TODO *) ls (* [dfa_initial_state n] calcule l'état initial de l'automate déterminisé. *) let dfa_initial_state (n: nfa) : dfa_state = + (* TODO *) Set.empty (* Construction de la table de transitions de l'automate DFA. *) @@ -212,7 +152,7 @@ let dfa_initial_state (n: nfa) : dfa_state = [(c1,q1);(c1,q2);...;(c1,qn);(c2,q'1);...(c2,q'm)] -> [(c1,{q1,q2,...,qn});(c2,{q'1,...,q'm})] : [assoc_merge_vals] - on applique la epsilon-fermeture sur tous les états: - [(c1,q1);...;(cn,qn)] -> [(c1, eps(q1)); ...; (cn, eps(qn))] : + [(c1,{q1,q2,...,qn});...;(cn,{qn}])] -> [(c1, eps({q1,q2,...,qn})); ...; (cn, eps({qn}))] : [epsilon_closure_set] On obtient alors l'ensemble des transitions depuis l'état [q] dans @@ -228,6 +168,11 @@ let assoc_throw_none (l : ('a option * 'b) list) : ('a * 'b) list = | Some x -> Some (x,n) ) l +let assoc_distribute_key (l : ('a set * 'b) list) : ('a * 'b) list = + List.fold_left (fun (acc : ('a * 'b) list) (k, v) -> + Set.fold (fun c acc -> (c, v)::acc) k acc) + [] l + let assoc_merge_vals (l : ('a * 'b) list) : ('a * 'b set) list = List.fold_left (fun (acc : ('a * 'b set) list) (k, v) -> match List.assoc_opt k acc with @@ -235,19 +180,16 @@ let assoc_merge_vals (l : ('a * 'b) list) : ('a * 'b set) list = | Some vl -> (k, Set.add v vl)::List.remove_assoc k acc ) [] l -let assoc_distribute_key (l : ('a set * 'b) list) : ('a * 'b) list = - List.fold_left (fun (acc : ('a * 'b) list) (k, v) -> - Set.fold (fun c acc -> (c, v)::acc) k acc) - [] l - - let rec build_dfa_table (table: (dfa_state, (char * dfa_state) list) Hashtbl.t) (n: nfa) (ds: dfa_state) : unit = match Hashtbl.find_option table ds with | Some _ -> () | None -> + (* [transitions] contient les transitions du DFA construites + * à partir des transitions du NFA comme décrit auparavant *) let transitions : (char * dfa_state) list = + (* TODO *) [] in Hashtbl.replace table ds transitions; @@ -283,12 +225,14 @@ let priority t = (* [min_priority l] renvoie le token de [l] qui a la plus petite priorité, ou [None] si la liste [l] est vide. *) let min_priority (l: token list) : token option = + (* TODO *) None (* [dfa_final_states n dfa_states] renvoie la liste des états finaux du DFA, accompagnés du token qu'ils reconnaissent. *) let dfa_final_states (n: nfa) (dfa_states: dfa_state list) : (dfa_state * (string -> token option)) list = + (* TODO *) [] (* Construction de la relation de transition du DFA. *) @@ -297,6 +241,7 @@ let dfa_final_states (n: nfa) (dfa_states: dfa_state list) : est la table générée par [build_dfa_table], définie ci-dessus. *) let make_dfa_step (table: (dfa_state, (char * dfa_state) list) Hashtbl.t) = fun (q: dfa_state) (a: char) -> + (* TODO *) None (* Finalement, on assemble tous ces morceaux pour construire l'automate. La @@ -473,99 +418,11 @@ let dfa_to_dot oc (n : dfa) (cl: char list): unit = ) n.dfa_states; Printf.fprintf oc "}\n" - - let alts l = match l with [] -> Eps | a::r -> List.fold_left (fun acc r -> Alt(acc,r)) a l -let list_regexp = - let lowercase_letters = "abcdefghijklmnopqrstuvwxyz" in - let uppercase_letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" in - let digits = "0123456789" in - let other_characters = "?!=<>_ ;,{}()[]-+*/%\n\t" in - let alphabet = char_list_of_string (lowercase_letters ^ uppercase_letters ^ digits ^ other_characters) in - let letter_regexp = char_range (char_list_of_string (uppercase_letters ^ lowercase_letters)) in - let digit_regexp = char_range (char_list_of_string digits) in - let keyword_regexp s = str_regexp (char_list_of_string s) in - [ - (keyword_regexp "while", fun s -> Some (SYM_WHILE)); - (keyword_regexp "int", fun s -> Some (SYM_INT)); - (keyword_regexp "void", fun s -> Some (SYM_VOID)); - (keyword_regexp "char", fun s -> Some (SYM_CHAR)); - (keyword_regexp "if", fun s -> Some (SYM_IF)); - (keyword_regexp "else", fun s -> Some (SYM_ELSE)); - (keyword_regexp "return", fun s -> Some (SYM_RETURN)); - (keyword_regexp "print", fun s -> Some (SYM_PRINT)); - (keyword_regexp "struct", fun s -> Some (SYM_STRUCT)); - (keyword_regexp ".", fun s -> Some (SYM_POINT)); - (keyword_regexp "+", fun s -> Some (SYM_PLUS)); - (keyword_regexp "-", fun s -> Some (SYM_MINUS)); - (keyword_regexp "*", fun s -> Some (SYM_ASTERISK)); - (keyword_regexp "/", fun s -> Some (SYM_DIV)); - (keyword_regexp "%", fun s -> Some (SYM_MOD)); - (keyword_regexp "{", fun s -> Some (SYM_LBRACE)); - (keyword_regexp "}", fun s -> Some (SYM_RBRACE)); - (keyword_regexp "[", fun s -> Some (SYM_LBRACKET)); - (keyword_regexp "]", fun s -> Some (SYM_RBRACKET)); - (keyword_regexp "(", fun s -> Some (SYM_LPARENTHESIS)); - (keyword_regexp ")", fun s -> Some (SYM_RPARENTHESIS)); - (keyword_regexp ";", fun s -> Some (SYM_SEMICOLON)); - (keyword_regexp ",", fun s -> Some (SYM_COMMA)); - (keyword_regexp "=", fun s -> Some (SYM_ASSIGN)); - (keyword_regexp "==", fun s -> Some (SYM_EQUALITY)); - (keyword_regexp "!=", fun s -> Some (SYM_NOTEQ)); - (keyword_regexp "<", fun s -> Some (SYM_LT)); - (keyword_regexp ">", fun s -> Some (SYM_GT)); - (keyword_regexp "<=", fun s -> Some (SYM_LEQ)); - (keyword_regexp ">=", fun s -> Some (SYM_GEQ)); - (Cat(letter_regexp, - Star(Alt(letter_regexp, - Alt(digit_regexp, - Charset (Set.singleton '_'))))), - fun s -> Some (SYM_IDENTIFIER s)); - (Cat(keyword_regexp "//", - Cat(Star (char_range (List.filter (fun c -> c <> '\n') alphabet)), - Alt (char_regexp '\n', Eps))), - fun s -> None); - (Cat(keyword_regexp "/*", - Cat( - Star (Alt (char_range (List.filter (fun c -> c <> '*') alphabet), - Cat (char_regexp '*', - char_range (List.filter (fun c -> c <> '/') alphabet)))), - keyword_regexp "*/")), - fun s -> None); - (Cat (char_regexp '\'', - Cat (char_range (List.filter (fun c -> c <> '\'' && c <> '\\') alphabet), - char_regexp '\'')), - fun s -> Some (SYM_CHARACTER (String.get s 1))); - (Cat (char_regexp '\'', Cat (char_regexp '\\', - Cat (char_range (char_list_of_string "\\tn0"), - char_regexp '\''))), - fun s -> match String.get s 2 with - | '\\' -> Some (SYM_CHARACTER '\\') - | 'n' -> Some (SYM_CHARACTER '\n') - | 't' -> Some (SYM_CHARACTER '\t') - | '0' -> Some (SYM_CHARACTER '\x00') - | _ -> None - ); - (Cat (char_regexp '"', - Cat (Star ( - Alt ( - char_range (List.filter (fun c -> c <> '"' && c <> '\\') alphabet), - Cat (char_regexp '\\', char_range (char_list_of_string "tn0\\\"")) - ) - ), - char_regexp '"')), - fun s -> Some (SYM_STRING (Stdlib.Scanf.unescaped (String.slice ~first:1 ~last:(-1) s)))); - (char_regexp ' ', fun s -> None); - (char_regexp '\n', fun s -> None); - (char_regexp '\t', fun s -> None); - (plus digit_regexp, fun s -> Some (SYM_INTEGER (int_of_string s))); - (Eps, fun s -> Some (SYM_EOF)) - ] - let nfa_of_list_regexp l = let (n, fs) = List.fold_left (fun (nfa, fs) (r,t) -> let n,fs = nfa_of_regexp r fs t in diff --git a/src/utils.ml b/src/utils.ml index 24f00f331f80e88f26a54d5dcfd5f8c6936ef7f4..3fa505c8085735f0bf9bbb9e91668eb23d8f065c 100644 --- a/src/utils.ml +++ b/src/utils.ml @@ -251,3 +251,29 @@ let assoc_err ?word:(word="item") k l = | Some v -> OK v | None -> Error (Format.sprintf "%s %s not found." word k) + +let remove_dups l : 'a list = + List.fold_left (fun acc elt -> if List.mem elt acc then acc else elt::acc) [] l + +let rec take n l = + if n = 0 then [] + else match l with + | [] -> [] + | a::r -> a::take (n-1) r + +let char_list_of_string l : char list = + String.to_list l + +let string_of_char_list cl = + String.of_list cl + +let string_of_char_set s = + string_of_char_list (Set.to_list s) + +let string_of_int_list l = + Printf.sprintf "%s" (String.concat "_" (List.map string_of_int l)) + +let string_of_int_set s = + string_of_int_list (Set.to_list s) + +