Exercices
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.
-
Écrire un analyseur lexical
qui puisse
reconnaître les commentaires d'Objective CAML. Ceux-ci commencent par un
(*
et se terminent par un *)
. Attention les commentaires
sont bien balancés, c'est-à-dire que le nombre d'ouvertures est égal
au nombre de fermetures.
(** fichier comment1.mll **)
{
let
ignore_lex
lb
=
ignore
(Lexing.lexeme
lb)
let
traite_normal
=
ignore_lex
let
traite_comment
=
ignore_lex
exception
Commentaires_mal_balances
}
rule
normal
=
parse
"(*"
{
ignore_lex
lexbuf
;
commentaire
lexbuf
;
normal
lexbuf
}
|
"*)"
{
raise
Commentaires_mal_balances
}
|
_
{
traite_normal
lexbuf
;
normal
lexbuf
}
|
eof
{
()
}
and
commentaire
=
parse
"(*"
{
ignore_lex
lexbuf
;
commentaire
lexbuf
;
commentaire
lexbuf
}
|
"*)"
{
ignore_lex
lexbuf
}
|
_
{
traite_comment
lexbuf
;
commentaire
lexbuf
}
|
eof
{
raise
Commentaires_mal_balances
}
- Écrire un programme
qui prend un fichier, le lit, retire les
commentaires de son texte puis crée un nouveau fichier résultat.
(** fichier comment2.mll **)
{
let
ignore_lex
lb
=
ignore
(Lexing.lexeme
lb)
let
sortie
=
ref
stdout
let
init_sortie
f
=
sortie
:=
f
let
traite_normal
lb
=
output_string
!
sortie
(Lexing.lexeme
lb)
let
traite_comment
=
ignore_lex
exception
Commentaires_mal_balances
}
rule
normal
=
parse
"(*"
{
ignore_lex
lexbuf
;
commentaire
lexbuf
;
normal
lexbuf
}
|
"*)"
{
raise
Commentaires_mal_balances
}
|
_
{
traite_normal
lexbuf
;
normal
lexbuf
}
|
eof
{
()
}
and
commentaire
=
parse
"(*"
{
ignore_lex
lexbuf
;
commentaire
lexbuf
;
commentaire
lexbuf
}
|
"*)"
{
ignore_lex
lexbuf
}
|
_
{
traite_comment
lexbuf
;
commentaire
lexbuf
}
|
eof
{
raise
Commentaires_mal_balances
}
{
let
decommente
src
dest
=
let
file_in
=
open_in
src
in
let
lb
=
Lexing.from_channel
file_in
in
let
file_out
=
open_out
dest
in
init_sortie
file_out
;
normal
lb
;
close_in
file_in
;
close_out
file_out
;;
let
usage
()
=
print_string
"comment2 filein fileout"
;
print_newline()
;;
let
main
()
=
if
Array.length
(Sys.argv)
<>
3
then
usage
()
else
decommente
Sys.argv.
(1
)
Sys.argv.
(2
)
;;
main
();;
}
- En Objective CAML les chaînes de caractères peuvent contenir
n'importe quel caractères, y compris les suites
(*
ou
*)
. Par exemple la chaîne de caractères
"un te(*xte quelco*)nque"
ne doit pas être considéré comme un commentaire.
Modifier
l'analyseur lexical pour
prendre en compte les chaînes de caractères.
(** fichier comment3.mll **)
{
let
ignore_lex
lb
=
ignore
(Lexing.lexeme
lb)
let
sortie
=
ref
stdout
let
init_sortie
f
=
sortie
:=
f
let
traite_normal
lb
=
output_string
!
sortie
(Lexing.lexeme
lb)
let
traite_comment
=
ignore_lex
exception
Commentaires_mal_balances
}
rule
normal
=
parse
"(*"
{
ignore_lex
lexbuf
;
commentaire
lexbuf
;
normal
lexbuf
}
|
"*)"
{
raise
Commentaires_mal_balances
}
|
'"'
{
traite_normal
lexbuf
;
chaine
lexbuf
;
normal
lexbuf
}
|
_
{
traite_normal
lexbuf
;
normal
lexbuf
}
|
eof
{
()
}
and
commentaire
=
parse
"(*"
{
ignore_lex
lexbuf
;
commentaire
lexbuf
;
commentaire
lexbuf
}
|
"*)"
{
ignore_lex
lexbuf
}
|
_
{
traite_comment
lexbuf
;
commentaire
lexbuf
}
|
eof
{
raise
Commentaires_mal_balances
}
and
chaine
=
parse
'"'
{
traite_normal
lexbuf
;
()
}
|
"\\\""
{
traite_normal
lexbuf
;
chaine
lexbuf
}
|
_
{
traite_normal
lexbuf
;
chaine
lexbuf
}
- Utiliser ce nouvel analyseur pour enlever les commentaires d'un
programme
Objective CAML.
(** fichier comment4.mll **)
{
let
ignore_lex
lb
=
ignore
(Lexing.lexeme
lb)
let
sortie
=
ref
stdout
let
init_sortie
f
=
sortie
:=
f
let
traite_normal
lb
=
output_string
!
sortie
(Lexing.lexeme
lb)
let
traite_comment
=
ignore_lex
exception
Commentaires_mal_balances
}
rule
normal
=
parse
"(*"
{
ignore_lex
lexbuf
;
commentaire
lexbuf
;
normal
lexbuf
}
|
"*)"
{
raise
Commentaires_mal_balances
}
|
'"'
{
traite_normal
lexbuf
;
chaine
lexbuf
;
normal
lexbuf
}
|
_
{
traite_normal
lexbuf
;
normal
lexbuf
}
|
eof
{
()
}
and
commentaire
=
parse
"(*"
{
ignore_lex
lexbuf
;
commentaire
lexbuf
;
commentaire
lexbuf
}
|
"*)"
{
ignore_lex
lexbuf
}
|
_
{
traite_comment
lexbuf
;
commentaire
lexbuf
}
|
eof
{
raise
Commentaires_mal_balances
}
and
chaine
=
parse
'"'
{
traite_normal
lexbuf
;
()
}
|
"\\\""
{
traite_normal
lexbuf
;
chaine
lexbuf
}
|
_
{
traite_normal
lexbuf
;
chaine
lexbuf
}
{
let
decommente
src
dest
=
let
file_in
=
open_in
src
in
let
lb
=
Lexing.from_channel
file_in
in
let
file_out
=
open_out
dest
in
init_sortie
file_out
;
normal
lb
;
close_in
file_in
;
close_out
file_out
;;
let
usage
()
=
print_string
"comment2 filein fileout"
;
print_newline()
;;
let
main
()
=
if
Array.length
(Sys.argv)
<>
3
then
usage
()
else
decommente
Sys.argv.
(1
)
Sys.argv.
(2
)
;;
main
();;
}
Évaluateur
On utilise ocamlyacc pour réaliser un évaluateur
d'expressions. L'idée est d'associer directement l'évaluation des
expressions aux règles de grammaire.
On choisit un langage d'expressions arithmétiques préfixées (et
complètement parenthésées) avec des opérateurs d'arité
variable. Par exemple, l'expression (ADD e1 e2 .. en) est
équivalente à e1 + e2 + .. + en. Les opérateurs
d'addition et de multiplication sont associatifs à droite et ceux de
soustraction et de division associatifs à gauche.
- Définir dans un fichier
opn_parser.mly
l'analyse syntaxique et les
règles d'évaluation d'une expression.
%{
let
rec
app_right
f
xs
=
match
xs
with
[
x]
->
x
|
x::xs
->
f
x
(app_right
f
xs)
|
_
->
failwith"missing argument"
;;
let
rec
app_left
f
xs
=
match
xs
with
[
x]
->
x
|
x1::x2::xs
->
app_left
f
((f
x1
x2)::xs)
|
_
->
failwith"missing argument"
;;
let
t
=
Hashtbl.create
3
;;
(
Hashtbl.add
t
"ADD"
(app_right
(+.
));
Hashtbl.add
t
"SUB"
(app_left
(-.
));
Hashtbl.add
t
"MUL"
(app_right
(
*.
));
Hashtbl.add
t
"DIV"
(app_left
(/.
))
)
;;
let
apply
o
vs
=
try
(Hashtbl.find
t
o)
vs
with
Not_found
->
(Printf.eprintf"Unknown operator %s\n"
o;
exit(1
))
;;
%}
%
token
Lpar
Rpar
%
token
<
float>
Num
%
token
<
string>
Atom
%
start
term
%
type
<
float>
term
%
type
<
float
list>
terms
%%
term
:
Num
{
$
1
}
|
Lpar
Atom
terms
Rpar
{
(apply
$
2
$
3
)
}
;
terms
:
term
{
[$
1
]
}
|
term
terms
{
$
1
::$
2
}
;
%%
- Définir dans un fichier opn_lexer.mll
l'analyse lexicale des expressions.
{
open
Opn_parser
}
rule
lexer
=
parse
[
' '
'\n'
]
{
lexer
lexbuf
}
|
'('
{
Lpar
}
|
')'
{
Rpar
}
|
'-'
?[
'0'
-
'9'
]*
'.'
?[
'0'
-
'9'
]*
{
Num
(float_of_string
(Lexing.lexeme
lexbuf))
}
|
[
'A'
-
'z'
]+
{
Atom
(Lexing.lexeme
lexbuf)
}
- Écrire un petit programme
principal opn
qui lit une ligne sur l'entrée standard et affiche le résultat de
l'évaluation.
open
Opn_lexer
;;
open
Opn_parser
;;
Printf.printf"? "
;
flush
stdout;
let
buf
=
Lexing.from_string
(input_line
stdin)
in
Printf.printf
"= %f\n"
(Opn_parser.term
Opn_lexer.lexer
buf)
;;