#open "lexuniv";;
let analyseur_lexical = construire_analyseur
  ["false";"true";"("; ","; ")"; "["; "]"; "not"; "*"; "/"; "-"; "+";
   "="; "<>"; "<"; ">"; "<="; ">="; "and"; "or"; "if"; "then"; "else";
   "while"; "do"; "write"; "read"; "begin"; ";"; "end"; ":=";
   "integer"; "boolean"; "array"; "of"; ".."; "var"; ":";
   "procedure"; "function"; "program"];;
let lire_liste lire_élément séparateur =
  let rec lire_reste = function
      [< (stream_check
           (function lexème -> lexème = MC séparateur)) sép;
         lire_élément elt;
         lire_reste reste >] -> elt :: reste
    | [< >] -> [] in
  function [< lire_élément elt; lire_reste reste >] -> elt :: reste
         | [< >] -> [];;
let est_un_opérateur opérateurs = function
    MC op -> mem op opérateurs
  | _     -> false;;
  
let lire_opérateur opérateurs = function
  [< (stream_check (est_un_opérateur opérateurs)) (MC op) >] -> op;;

let lire_opération lire_base opérateurs =
  let rec lire_reste e1 = function
    [< (lire_opérateur opérateurs) op;
       lire_base e2;
       (lire_reste (Op_binaire(op, e1, e2))) e >] -> e
  | [< >] -> e1 in
 function [< lire_base e1; (lire_reste e1) e >] -> e;;
let rec lire_expr0 flux =
  match flux with
    [< 'Entier n >] -> Constante(Entière n)
  | [< 'MC "false" >] -> Constante(Booléenne false)
  | [< 'MC "true" >] -> Constante(Booléenne true)
  | [< 'Ident nom >] ->
      begin match flux with
        [< 'MC "("; (lire_liste lire_expr ",") el; 'MC ")">] ->
                 Application(nom, el)
      | [< >] -> Variable nom
      end
  | [< 'MC "("; lire_expr e; 'MC ")" >] -> e

and lire_expr1 flux =
  match flux with
    [< lire_expr0 e1 >] ->
      match flux with
        [< 'MC "["; lire_expr e2; 'MC "]" >] -> Accès_tableau(e1,e2)
      | [< >] -> e1

and lire_expr2 = function
    [< 'MC "-"; lire_expr1 e >] -> Op_unaire("-", e)
  | [< 'MC "not"; lire_expr1 e >] -> Op_unaire("not", e)
  | [< lire_expr1 e >] -> e

and lire_expr3 flux = 
  lire_opération lire_expr2 ["*"; "/"] flux
and lire_expr4 flux = 
  lire_opération lire_expr3 ["+"; "-"] flux
and lire_expr5 flux = 
  lire_opération lire_expr4 ["="; "<>"; "<"; ">"; "<="; ">="] flux
and lire_expr6 flux = 
  lire_opération lire_expr5 ["and"] flux
and lire_expr flux = 
  lire_opération lire_expr6 ["or"] flux;;
let rec lire_instr flux =
  match flux with
    [< 'MC "if"; lire_expr e1; 'MC "then"; lire_instr i2 >] ->
      begin match flux with
        [< 'MC "else"; lire_instr i3 >] -> If(e1, i2, i3)
      | [< >] -> If(e1, i2, Bloc [])
      end
  | [< 'MC "while"; lire_expr e1; 'MC "do"; lire_instr i2 >] ->
      While(e1, i2)
  | [< 'MC "write"; 'MC "("; lire_expr e; 'MC ")" >] ->
      Write e
  | [< 'MC "read"; 'MC "("; 'Ident nom; 'MC ")" >] ->
      Read nom
  | [< 'MC "begin"; (lire_liste lire_instr ";") il; 'MC "end" >] ->
      Bloc il
  | [< lire_expr e >] ->
      match e with
        Application(nom, el) ->
          Appel(nom, el)
      | Variable nom ->
          begin match flux with
            [< 'MC ":="; lire_expr e >] ->
              Affectation_var(nom, e)
          end
      | Accès_tableau(e1, e2) ->
          begin match flux with
            [< 'MC ":="; lire_expr e3 >] ->
              Affectation_tableau(e1, e2, e3)
          end
      | _ -> raise Parse_error;;

let rec lire_type = function
    [< 'MC "integer" >] -> Integer
  | [< 'MC "boolean" >] -> Boolean
  | [< 'MC "array"; 'MC "["; 'Entier bas; 'MC ".."; 'Entier haut;
       'MC "]"; 'MC "of"; lire_type ty >] -> Array(bas, haut, ty);;

let rec lire_variables = function
    [< 'MC "var"; 'Ident nom; 'MC ":"; lire_type ty; 'MC ";";
       lire_variables reste >] -> (nom,ty)::reste
  | [< >] -> [];;

let lire_un_paramètre = function
    [< 'Ident nom; 'MC ":"; lire_type ty >] -> (nom,ty);;

let lire_paramètres = function
    [< 'MC "(";
       (lire_liste lire_un_paramètre ",") paramètres;
       'MC ")" >] -> paramètres;;

let lire_procédure = function
  [< 'MC "procedure"; 'Ident nom; lire_paramètres p; 'MC ";";
     lire_variables v; lire_instr i; 'MC ";" >] ->
       (nom, {proc_paramètres=p; proc_variables=v; proc_corps=i});;

let lire_fonction = function
  [< 'MC "function"; 'Ident nom; lire_paramètres p; 'MC ":";
     lire_type ty; 'MC ";"; lire_variables v;
     lire_instr i; 'MC ";" >] ->
       (nom, {fonc_paramètres=p; fonc_type_résultat=ty;
              fonc_variables=v; fonc_corps=i});;

let rec lire_proc_fonc = function
    [< lire_procédure proc; lire_proc_fonc (procs, foncs) >] ->
      (proc::procs, foncs)
  | [< lire_fonction fonc;  lire_proc_fonc (procs, foncs) >] ->
       (procs, fonc::foncs)
  | [< >] -> ([], []);;

let lire_prog = function
    [< 'MC "program"; 'Ident nom_du_programme; 'MC ";";
       lire_variables v; lire_proc_fonc (p,f); lire_instr i >] ->
    { prog_variables=v; prog_procédures=p;
      prog_fonctions=f; prog_corps=i };;

let lire_programme flux = lire_prog (analyseur_lexical flux);;
