%{
openBasic_types
;;
letphrase_of_cmd
c
=
match
c
with
"RUN"
->
Run
|
"LIST"
->
List
|
"END"
->
End
|
_
->
failwith
"line : unexpected command"
;;
letop_bin_of_rel
r
=
match
r
with
"="
->
EGAL
|
"<"
->
INF
|
"<="
->
INFEQ
|
">"
->
SUP
|
">="
->
SUPEQ
|
"<>"
->
DIFF
|
_
->
failwith
"line : unexpected relation symbol"
;;%}
Les unités lexicales sont les suivantes :
Leurs noms parlent d'eux-mêmes et ils sont décrits par le fichier basic_lexer.mll (voir page ??).
%
token
<
int>
Lint
%
token
<
string>
Lident
%
token
<
string>
Lstring
%
token
<
string>
Lcmd
%
tokenLplus
Lmoins
Lmult
Ldiv
Lmod
%
token
<
string>
Lrel
%
tokenLand
Lor
Lneg
%
tokenLpar
Rpar
%
token
<
string>
Lrem
%
tokenLrem
Llet
Lprint
Linput
Lif
Lthen
Lgoto
%
tokenLegal
%
tokenLeol
Les règles de priorité entre opérateurs reprennent les valeurs données par les fonctions priority_ob et priority_ou définies lorsque nous avions donnée la grammaire de notre Basic (voir page ??).
Le symbole Lopp nous servira à traiter le moins unaire. Ce n'est pas un terminal de la grammaire, mais un << pseudo non terminal >> permettant de gérer la surcharge entre opérateurs lorsque deux utilisations d'un même symbole ne doivent pas avoir la même priorité selon le contexte. Et c'est le cas du symbole moins (-). Nous reviendrons sur ce point lorsque nous aurons énoncé les règles de la grammaire.
%
rightLneg
%
leftLand
Lor
%
leftLegal
Lrel
%
leftLmod
%
leftLplus
Lmoins
%
leftLmult
Ldiv
%
nonassocLopp
Le non terminal axiome est line. La fonction engendrée retournera l'arbre de syntaxe abstraite de la ligne analysée.
%start line %type <Basic_types.phrase> line
Ces règles n'appellent pas de commentaire particulier sauf celle-ci :
%%
line
:
Lint
inst
Leol
{
Ligne
{num
=$
1
;inst
=$
2
}}
|
Lcmd
Leol
{
phrase_of_cmd
$
1
}
;
inst
:
Lrem
{
Rem
$
1
}
|
Lgoto
Lint
{
Goto
$
2
}
|
Lprint
exp
{
$
2
}
|
Linput
Lident
{
Input
$
2
}
|
Lif
exp
Lthen
Lint
{
If
(
$
2
,
$
4
)}
|
Llet
Lident
Legal
exp
{
Let
(
$
2
,
$
4
)}
;
exp
:
Lint
{
ExpInt
$
1
}
|
Lident
{
ExpVar
$
1
}
|
Lstring
{
ExpStr
$
1
}
|
Lneg
exp
{
ExpUnr
(NON
,
$
2
)}
|
exp
Lplus
exp
{
ExpBin
(
$
1
,
PLUS
,
$
3
)}
|
exp
Lmoins
exp
{
ExpBin
(
$
1
,
MOINS
,
$
3
)}
|
exp
Lmult
exp
{
ExpBin
(
$
1
,
MULT
,
$
3
)}
|
exp
Ldiv
exp
{
ExpBin
(
$
1
,
DIV
,
$
3
)}
|
exp
Lmod
exp
{
ExpBin
(
$
1
,
MOD
,
$
3
)}
|
exp
Legal
exp
{
ExpBin
(
$
1
,
EGAL
,
$
3
)}
|
exp
Lrel
exp
{
ExpBin
(
$
1
,
(op_bin_of_rel
$
2
),
$
3
)}
|
exp
Land
exp
{
ExpBin
(
$
1
,
ET
,
$
3
)}
|
exp
Lor
exp
{
ExpBin
(
$
1
,
OU
,
$
3
)}
|
Lmoins
exp
%
precLopp
{
ExpUnr(OPPOSE
,
$
2
)}
|
Lpar
exp
Rpar
{
$
2
}
;
%%
exp : ... | Lmoins exp %prec Lopp { ExpUnr(OPPOSE, $2) }Elle concerne l'utilisation unaire du symbole -. Le mot clé
%prec
que l'on trouve dans cette règle indique que
cette construction doit recevoir la priorité de Lopp (ici, la
priorité maximale).Remarquons que nous avons dû isoler le symbole = qui sert à la fois dans les expressions et dans l'instruction d'affectation.
{open
Basic_parser
;;
let
string_chars
s
=
String.sub
s
1
((String.length
s)
-
2
);;
}
rulelexer
=
parse
[
' '
'\t'
]
{
lexer
lexbuf
}
|
'\n'
{
Leol
}
|
'!'
{
Lneg
}
|
'&'
{
Land
}
|
'|'
{
Lor
}
|
'='
{
Legal
}
|
'%'
{
Lmod
}
|
'+'
{
Lplus
}
|
'-'
{
Lmoins
}
|
'*'
{
Lmult
}
|
'/'
{
Ldiv
}
|
[
'<'
'>'
]
{
Lrel
(Lexing.lexeme
lexbuf)
}
|
"<="
{
Lrel
(Lexing.lexeme
lexbuf)
}
|
">="
{
Lrel
(Lexing.lexeme
lexbuf)
}
|
"REM"
[
^
'\n'
]*
{
Lrem
(Lexing.lexeme
lexbuf)
}
|
"LET"
{
Llet
}
|
"PRINT"
{
Lprint
}
|
"INPUT"
{
Linput
}
|
"IF"
{
Lif
}
|
"THEN"
{
Lthen
}
|
"GOTO"
{
Lgoto
}
|
"RUN"
{
Lcmd
(Lexing.lexeme
lexbuf)
}
|
"LIST"
{
Lcmd
(Lexing.lexeme
lexbuf)
}
|
"END"
{
Lcmd
(Lexing.lexeme
lexbuf)
}
|
[
'0'
-
'9'
]+
{
Lint
(int_of_string
(Lexing.lexeme
lexbuf))
}
|
[
'A'
-
'z'
]+
{
Lident
(Lexing.lexeme
lexbuf)
}
|
'"'
[
^
'"'
]*
'"'
{
Lstring
(string_chars
(Lexing.lexeme
lexbuf))
}
"REM" [^ '\n']*
). Elle reconnaît le mot clé REM suivi par un nombre quelconque de caractères différents du retour
chariot. La seconde concerne les chaîne de caractères
('"' [^ '"']* '"'
) considérées comme une suite de caractères
différents de "
et encadrée par des "
.ocamlc -c basic_types.mli ocamlyacc basic_parser.mly ocamllex basic_lexer.mll ocamlc -c basic_parser.mli ocamlc -c basic_lexer.ml ocamlc -c basic_parser.mlCe qui engendrera les fichiers basic_lexer.cmo et basic_parser.cmo qui pourrons être intégrés à l'application.
par
match
parse
(input_line
stdin)
with
Remarquons que nous devons remettre en fin de ligne le caractère
match
line
lexer
(Lexing.from_string
((input_line
stdin)
^
"\n"
))with
'\n'
que la fonction input_line avait
supprimé. Ceci est nécessaire, car nous utilisons ce caractère pour
marquer la fin d'une ligne de commande (Leol).