diff --git a/dune-project b/dune-project index 269f44852..6961e3afe 100644 --- a/dune-project +++ b/dune-project @@ -51,3 +51,17 @@ (= 0.19.0)) (parmap (= 1.2.3)))) + +(package + (name irj_parser) + (synopsis "Parser for the IRJ tests") + (description "This parser is aimed for the tests used by la DGFiP to test the calculation of the French income tax") + (depends + (ocaml + (and(>= "4.11.2"))) + (dune + (and :build)) + (odoc + (= 1.5.3)) + (ocamlformat + (= 0.19.0)))) diff --git a/irj_parser.opam b/irj_parser.opam new file mode 100644 index 000000000..1ca3bb977 --- /dev/null +++ b/irj_parser.opam @@ -0,0 +1,32 @@ +# This file is generated by dune, edit dune-project instead +opam-version: "2.0" +version: "1.1.0" +synopsis: "Parser for the IRJ tests" +description: + "This parser is aimed for the tests used by la DGFiP to test the calculation of the French income tax" +maintainer: ["denis.merigoux@inria.fr"] +authors: ["Denis Merigoux" "Raphaël Monat"] +license: "GPL-3.0-or-later" +homepage: "https://gitlab.inria.fr/verifisc/mlang" +bug-reports: "https://gitlab.inria.fr/verifisc/mlang/issues" +depends: [ + "ocaml" {>= "4.11.2"} + "dune" {build} + "odoc" {= "1.5.3"} + "ocamlformat" {= "0.19.0"} +] +build: [ + ["dune" "subst"] {pinned} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] +] +dev-repo: "git+https://gitlab.inria.fr/verifisc/mlang.git" diff --git a/src/irj_parser/dune b/src/irj_parser/dune new file mode 100644 index 000000000..c899620eb --- /dev/null +++ b/src/irj_parser/dune @@ -0,0 +1,14 @@ +(env + (static + (ocamlopt_flags + (-O3 -ccopt -static)))) + +(library + (name irj_include) + (public_name irj_parser)) + +(ocamllex irj_lexer) + +(menhir + (modules irj_parser) + (flags --strict --explain)) diff --git a/src/irj_parser/irj_ast.ml b/src/irj_parser/irj_ast.ml new file mode 100644 index 000000000..1e03092b5 --- /dev/null +++ b/src/irj_parser/irj_ast.ml @@ -0,0 +1,76 @@ +(* Copyright Inria, contributors: Raphaël Monat (2019) + Mathieu Durero , David Declerck (2023) + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + this program. If not, see . *) + +type pos = { + pos_filename : string; + pos_loc : Lexing.position * Lexing.position; +} + +let mk_position sloc = + { pos_filename = (fst sloc).Lexing.pos_fname; pos_loc = sloc } + +exception TestParsingError of (string * pos) +(* duplication of some of the utils *) + +type literal = I of int | F of float + +type var_value = string * literal * pos + +(* type var_values = var_value list *) + +type calc_error = string * pos + +(* type calc_errors = calc_error list *) + +(* type rappel = string * string * var_value * string * string * string * string + * string *) +type rappel = { + event_nb : int; + rappel_nb : int; + variable_code : string; + change_value : int; + direction : string; + (* R, C, M, P *) + penalty_code : int option; + (* 0 - 99 *) + base_tolerance_legale : int option; + month_year : int; + (* MMYYYY *) + decl_2042_rect : int option; + (* 0 or 1 *) + pos : pos; +} + +type prim_data_block = { + entrees : var_value list; + controles_attendus : calc_error list; + resultats_attendus : var_value list; +} + +type corr_data_block = { + entrees_rappels : rappel list; + controles_attendus : calc_error list; + resultats_attendus : var_value list; +} + +type irj_file = { + nom : string; + prim : prim_data_block; + rapp : corr_data_block option; + (* corr : prim_data_block option; *) + (*corr is for old correctif form from primitif files, rapp is for the + actual one in correctif files*) +} diff --git a/src/irj_parser/irj_file.ml b/src/irj_parser/irj_file.ml new file mode 100644 index 000000000..edc84f425 --- /dev/null +++ b/src/irj_parser/irj_file.ml @@ -0,0 +1,41 @@ +(* Copyright Inria, contributors: Raphaël Monat (2019) + Mathieu Durero (2023) + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + this program. If not, see . *) + +open Irj_ast + +let parse_file (test_name : string) : irj_file = + let input = open_in test_name in + let filebuf = Lexing.from_channel input in + let filebuf = + { + filebuf with + lex_curr_p = { filebuf.lex_curr_p with pos_fname = test_name }; + } + in + let f = + try Irj_parser.irj_file Irj_lexer.token filebuf with + | TestParsingError e -> + close_in input; + raise (TestParsingError e) + | Irj_parser.Error -> + close_in input; + raise + (TestParsingError + ( "Test syntax error", + mk_position (filebuf.lex_start_p, filebuf.lex_curr_p) )) + in + close_in input; + f diff --git a/src/mlang/test_framework/test_ast.ml b/src/irj_parser/irj_file.mli similarity index 75% rename from src/mlang/test_framework/test_ast.ml rename to src/irj_parser/irj_file.mli index 88c1d9f38..c0dab0c7f 100644 --- a/src/mlang/test_framework/test_ast.ml +++ b/src/irj_parser/irj_file.mli @@ -1,4 +1,5 @@ (* Copyright Inria, contributors: Raphaël Monat (2019) + Mathieu Durero (2023) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -13,14 +14,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *) -type literal = I of int | F of float - -type var_values = (string * literal * Pos.t) list - -type test_file = { - nom : string; - ep : var_values; - cp : var_values; - rp : var_values; - corr : (var_values * var_values * var_values) option; -} +val parse_file : string -> Irj_ast.irj_file +(** [parse_file file] loads the content of a given IRJ [file] in a simple + datastructure. *) diff --git a/src/irj_parser/irj_lexer.mll b/src/irj_parser/irj_lexer.mll new file mode 100644 index 000000000..200a38c04 --- /dev/null +++ b/src/irj_parser/irj_lexer.mll @@ -0,0 +1,109 @@ +(* Copyright Inria, contributors: Raphaël Monat (2019) + David Declerck (2023) + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + this program. If not, see . *) + +{ +open Lexing +open Irj_parser +open Irj_ast + +let error lb msg = + raise (TestParsingError ("Lexing error : " ^ msg, + mk_position (Lexing.lexeme_start_p lb, Lexing.lexeme_end_p lb))) + +module StrMap = Map.Make (String) + +let keywords = + List.fold_left (fun map (kw, tok) -> + StrMap.add kw tok map + ) StrMap.empty [ + "#NOM", NOM; + "#FIP", FIP; + "#ENTREES-PRIMITIF", ENTREESPRIM; + "#CONTROLES-PRIMITIF", CONTROLESPRIM; + "#RESULTATS-PRIMITIF", RESULTATSPRIM; + "#ENTREES-CORRECTIF", ENTREESCORR; + "#CONTROLES-CORRECTIF", CONTROLESCORR; + "#RESULTATS-CORRECTIF", RESULTATSCORR; + "#ENTREES-RAPPELS", ENTREESRAPP; + "#CONTROLES-RAPPELS", CONTROLESRAPP; + "#RESULTATS-RAPPELS", RESULTATSRAPP; + "#DATES", DATES; + "#AVIS_IR", AVISIR; + "#AVIS_CSG", AVISCSG; + ] + +let is_bol lb = + (* bol = beginning of line *) + lb.lex_start_p.pos_cnum - lb.lex_start_p.pos_bol = 0 + +let check_cr lb = + if String.contains (lexeme lb) '\r' then + error lb ("Carriage return detected") + (* No more supposed to be an error in autotests. Keeping it to enforce it later? *) +} + +let blank = [' ' '\t'] +let any = [^ '\n'] +let nl = ['\n'] + +rule token = parse + +| '\n' | "\r\n" + { check_cr lexbuf; new_line lexbuf; + if is_bol lexbuf then token lexbuf + else NL } + +| '*' any* nl + { check_cr lexbuf; new_line lexbuf; + if is_bol lexbuf then token lexbuf + else error lexbuf "Comment with * must start in the first column" } + +| blank any* nl + { check_cr lexbuf; new_line lexbuf; + if is_bol lexbuf then error lexbuf "Line can not start with a blank" + else NL } + +| '-'? ['0' - '9']+ as i + { INTEGER (int_of_string i) } + +| '-'? ['0' - '9']+ '.' ['0' - '9']* as f + { FLOAT (float_of_string f) } (* DONT KEEP THAT *) + (* Probably in order to write a specific function for our number format *) + +| ['a'-'z' 'A'-'Z' '0'-'9' '_']+ as s + { SYMBOL s } + +| ['a'-'z' 'A'-'Z' '0'-'9' '_' '-' '.' ';' (*' '*)]+ as s + { NAME s } + (* Compared to the old lexer, adds _ and . removes space *) + +| "/" + { SLASH } + +| "##" + { ENDSHARP } + +| "#" ['a'-'z' 'A'-'Z' '0'-'9' '_' '-']+ as s + { match StrMap.find_opt s keywords with + | None -> error lexbuf (Printf.sprintf "Unknown section name: '#%s'" s) + | Some t -> t } + +| eof + { EOF } + +| _ as c + { error lexbuf (Printf.sprintf + "Unexpected character '%c' (%d)" c (Char.code c)) } diff --git a/src/irj_parser/irj_parser.mly b/src/irj_parser/irj_parser.mly new file mode 100644 index 000000000..88675a680 --- /dev/null +++ b/src/irj_parser/irj_parser.mly @@ -0,0 +1,144 @@ +(* Copyright Inria, contributors: Raphaël Monat (2019) + Mathieu Durero (2023) + David Declerck (2023) + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + this program. If not, see . *) + +%{ open Irj_ast + + let error (sp, ep) msg = + raise (TestParsingError ("Parse error : " ^ msg, mk_position (sp, ep))) +%} + +%token SYMBOL NAME +%token INTEGER +%token FLOAT + +%token SLASH +/* Used as field separator */ +%token NOM FIP +/* Identifiers */ +%token ENTREESPRIM CONTROLESPRIM RESULTATSPRIM +/* Primary computation data blocks */ +%token ENTREESRAPP CONTROLESRAPP RESULTATSRAPP +/* Corrective computation data blocks */ +%token ENTREESCORR CONTROLESCORR RESULTATSCORR +/* Old form of corrective data blocks, always present and empty in primary computation test files */ +%token DATES AVISIR AVISCSG +/* Old empty data blocks rarely found in primary files from 2019 and older */ +%token ENDSHARP +/* Mark the end of a record */ + +%token NL +/* New line */ +%token EOF + +%type irj_file + +%start irj_file + +%% + +irj_file: +| NOM NL + nom = list(name) + fip? + prim = primitif + rapp = rappels + endsharp NL + EOF { + let nom = + match nom with + | [n] -> n + | [] -> error $loc(nom) "Missing name in section #NOM" + | _ -> error $loc(nom) "Extra line(s) in section #NOM" + in + { nom; prim; rapp } } +| EOF { error $loc "Empty test file" } + +/* What's the point of this alternative?*/ +name: +| n = NAME NL { n } +| n = SYMBOL NL { n } + +fip: + FIP SLASH SYMBOL? NL { } (* it is actually allowed to leave it blank *) + +primitif: + ENTREESPRIM NL + entrees = list(variable_and_value) + CONTROLESPRIM NL + controles_attendus = list(calc_error) + RESULTATSPRIM NL + resultats_attendus = list(variable_and_value) + { { entrees; controles_attendus; resultats_attendus } } + +rappels: +/* The two constructions match respectively corrective test files and primary test files */ +| ENTREESRAPP NL + entrees_rappels = list(rappel) + CONTROLESRAPP NL + controles_attendus = list(calc_error) + RESULTATSRAPP NL + resultats_attendus = list(variable_and_value) + { Some { entrees_rappels; controles_attendus; resultats_attendus} } +| ENTREESCORR NL CONTROLESCORR NL RESULTATSCORR NL DATES? AVISIR? AVISCSG? { None } + +variable_and_value: +| var = SYMBOL SLASH value = value NL { (var, value, mk_position $sloc) } +| SYMBOL error { error $loc "Missing slash in pair variable/value" } + +calc_error: + error = SYMBOL NL { (error, mk_position $sloc) } + +rappel: + event_nb = integer SLASH + rappel_nb = integer SLASH + variable_code = SYMBOL SLASH + change_value = integer SLASH (* No decimal value was found in existing files *) + direction = SYMBOL SLASH + penalty_code = INTEGER? SLASH + base_tolerance_legale = INTEGER? SLASH + month_year = integer SLASH + decl_2042_rect = INTEGER? NL + { + if direction <> "R" && direction <> "C" && direction <> "M" && direction <> "P" then + error $loc(direction) ("Unknown value for 'direction' (type of the 'rappel', should be R, C, M or P) : " ^ direction); + let p = match penalty_code with Some p -> p | _ -> 0 in + if p < 0 || p > 99 then + error $loc(direction) ("Invalid value for 'penalty_code' (out of range 0-99) : " ^ (string_of_int p)); + {event_nb; + rappel_nb; + variable_code; + change_value; + direction; + penalty_code; + base_tolerance_legale; + month_year; + decl_2042_rect; + pos = mk_position $sloc } + } + +integer: +| i = INTEGER { i } +| error { error $loc "Missing integer" } + +value: +| i = INTEGER { I (i) } +| f = FLOAT { F (f) } +| error { error $loc "Missing numerical value" } + +endsharp: +| ENDSHARP { () } +| error { error $loc "Missing ## at end of file" } diff --git a/src/mlang/dune b/src/mlang/dune index ccce97d59..52665702d 100644 --- a/src/mlang/dune +++ b/src/mlang/dune @@ -8,7 +8,7 @@ (library (public_name mlang) (libraries ocamlgraph re ANSITerminal parmap cmdliner threads - dune-build-info num gmp)) + dune-build-info num gmp irj_parser)) (documentation (package mlang) diff --git a/src/mlang/test_framework/dune b/src/mlang/test_framework/dune deleted file mode 100644 index 9c35987bf..000000000 --- a/src/mlang/test_framework/dune +++ /dev/null @@ -1,4 +0,0 @@ -(ocamllex test_lexer) - -(menhir - (modules test_parser)) diff --git a/src/mlang/test_framework/test_interpreter.ml b/src/mlang/test_framework/test_interpreter.ml index 7c050055c..f262ce1b5 100644 --- a/src/mlang/test_framework/test_interpreter.ml +++ b/src/mlang/test_framework/test_interpreter.ml @@ -13,31 +13,15 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *) -open Test_ast +open Irj_include -let parse_file (test_name : string) : test_file = - let input = open_in test_name in - let filebuf = Lexing.from_channel input in - let filebuf = - { - filebuf with - lex_curr_p = { filebuf.lex_curr_p with pos_fname = test_name }; - } - in - let f = - try Test_parser.test_file Test_lexer.token filebuf with - | Errors.StructuredError e -> - close_in input; - raise (Errors.StructuredError e) - | Test_parser.Error -> - close_in input; - Errors.raise_spanned_error "Test syntax error" - (Parse_utils.mk_position (filebuf.lex_start_p, filebuf.lex_curr_p)) - in - close_in input; - f +let convert_pos (pos : Irj_ast.pos) = + Pos.make_position pos.pos_filename pos.pos_loc + +(* enforces type compatibility (the type Irj_ast.pos is defined in exactly the + same way as Pos.t) *) -let to_ast_literal (value : Test_ast.literal) : Mast.literal = +let to_ast_literal (value : Irj_ast.literal) : Mast.literal = match value with I i -> Float (float_of_int i) | F f -> Float f let find_var_of_name (p : Mir.program) (name : string Pos.marked) : @@ -58,22 +42,24 @@ let find_var_of_name (p : Mir.program) (name : string Pos.marked) : v2.Mir.Variable.execution_number) (Pos.VarNameToID.find name p.program_idmap)) -let to_MIR_function_and_inputs (program : Bir.program) (t : test_file) +let to_MIR_function_and_inputs (program : Bir.program) (t : Irj_ast.irj_file) (test_error_margin : float) : Bir_interface.bir_function * Mir.literal Bir.VariableMap.t = let func_variable_inputs, input_file = List.fold_left (fun (fv, in_f) (var, value, pos) -> let var = - find_var_of_name program.mir_program (var, pos) + find_var_of_name program.mir_program (var, convert_pos pos) |> Bir.(var_from_mir default_tgv) in let lit = - match value with I i -> Mir.Float (float_of_int i) | F f -> Float f + match value with + | Irj_ast.I i -> Mir.Float (float_of_int i) + | F f -> Float f in (Bir.VariableMap.add var () fv, Bir.VariableMap.add var lit in_f)) (Bir.VariableMap.empty, Bir.VariableMap.empty) - t.ep + t.prim.entrees in let func_constant_inputs = Bir.VariableMap.empty in let func_outputs = Bir.VariableMap.empty in @@ -87,35 +73,36 @@ let to_MIR_function_and_inputs (program : Bir.program) (t : test_file) two using the line below*) let var = Pos.unmark - (find_var_of_name program.mir_program (var, pos)) + (find_var_of_name program.mir_program (var, convert_pos pos)) .Mir.Variable.name in (* we allow a difference of 0.000001 between the control value and the result *) let first_exp = ( Mast.Comparison - ( (Lte, pos), + ( (Lte, convert_pos pos), ( Mast.Binop - ( (Mast.Sub, pos), - (Literal (Variable (Normal var)), pos), - (Literal (to_ast_literal value), pos) ), - pos ), - (Literal (Float test_error_margin), pos) ), - pos ) + ( (Mast.Sub, convert_pos pos), + (Literal (Variable (Normal var)), convert_pos pos), + (Literal (to_ast_literal value), convert_pos pos) ), + convert_pos pos ), + (Literal (Float test_error_margin), convert_pos pos) ), + convert_pos pos ) in let second_exp = ( Mast.Comparison - ( (Lte, pos), + ( (Lte, convert_pos pos), ( Mast.Binop - ( (Mast.Sub, pos), - (Literal (to_ast_literal value), pos), - (Literal (Variable (Normal var)), pos) ), - pos ), - (Literal (Float test_error_margin), pos) ), - pos ) + ( (Mast.Sub, convert_pos pos), + (Literal (to_ast_literal value), convert_pos pos), + (Literal (Variable (Normal var)), convert_pos pos) ), + convert_pos pos ), + (Literal (Float test_error_margin), convert_pos pos) ), + convert_pos pos ) in - (Mast.Binop ((Mast.And, pos), first_exp, second_exp), pos)) - t.rp) + ( Mast.Binop ((Mast.And, convert_pos pos), first_exp, second_exp), + convert_pos pos )) + t.prim.resultats_attendus) in ( { func_variable_inputs; func_constant_inputs; func_outputs; func_conds }, input_file ) @@ -125,7 +112,7 @@ let check_test (combined_program : Bir.program) (test_name : string) (round_ops : Cli.round_ops) (test_error_margin : float) : Bir_instrumentation.code_coverage_result = Cli.debug_print "Parsing %s..." test_name; - let t = parse_file test_name in + let t = Irj_file.parse_file test_name in Cli.debug_print "Running test %s..." t.nom; let f, input_file = to_MIR_function_and_inputs combined_program t test_error_margin diff --git a/src/mlang/test_framework/test_lexer.mll b/src/mlang/test_framework/test_lexer.mll deleted file mode 100644 index f8109b14a..000000000 --- a/src/mlang/test_framework/test_lexer.mll +++ /dev/null @@ -1,68 +0,0 @@ -(* -Copyright Inria, contributors: - Raphaël Monat (2019) - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*) - -{ -open Lexing -open Test_parser -} - -rule token = parse -| [' ' '\t'] (* also ignore newlines, not only whitespace and tabs *) - { token lexbuf } -| '*' [^ '\n']* '\n' (* ignore comments *) - { new_line lexbuf; token lexbuf } -| '\n' | "\r\n" - { new_line lexbuf; token lexbuf} -| "/" - { SLASH } -| "#NOM" - { NOM } -| "#FIP" - { FIP } -| "#ENTREES-PRIMITIF" - { ENTREESP } -| "#CONTROLES-PRIMITIF" - { CONTROLESP } -| "#RESULTATS-PRIMITIF" - { RESULTATSP } -| "#ENTREES-CORRECTIF" - { ENTREESC } -| "#CONTROLES-CORRECTIF" - { CONTROLESC } -| "#RESULTATS-CORRECTIF" - { RESULTATSC } -| "#DATES" - { DATES } -| "#AVIS_IR" - { AVISIR } -| "#AVIS_CSG" - { AVISCSG } -| "##" - { ENDSHARP } -| '-'? ['0' - '9']+ as i - { INTEGER i } -| '-'? ['0' - '9']+ '.' ['0' - '9']* as f - { FLOAT f } -| ['a'-'z' 'A'-'Z' '0'-'9' '_']+ as s - { SYMBOL s } -| ['a'-'z' 'A'-'Z' ' ' '0'-'9' ';' '-']+ as s - { NAME s } -| eof - { EOF } -| _ - { Errors.raise_spanned_error "Test file lexer error" (Parse_utils.mk_position (Lexing.lexeme_start_p lexbuf, Lexing.lexeme_end_p lexbuf)) } diff --git a/src/mlang/test_framework/test_parser.mly b/src/mlang/test_framework/test_parser.mly deleted file mode 100644 index 5af2d69f7..000000000 --- a/src/mlang/test_framework/test_parser.mly +++ /dev/null @@ -1,75 +0,0 @@ -(* -Copyright Inria, contributors: - Raphaël Monat (2019) - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*) - -%{ open Test_ast - open Parse_utils %} - -%token SYMBOL NAME INTEGER FLOAT - -%token SLASH -%token NOM FIP -%token ENTREESP CONTROLESP RESULTATSP -%token ENTREESC CONTROLESC RESULTATSC -%token DATES AVISIR AVISCSG -%token ENDSHARP - -%token EOF - -%type test_file - -%start test_file - -%% - - - -test_file: -| NOM nom = name - fip? - ENTREESP - ep = list(variable_and_value) - CONTROLESP - cp = list(variable_and_value) - RESULTATSP - rp = list(variable_and_value) - corr = correctif? - DATES? - AVISIR? - AVISCSG? - ENDSHARP { { nom; ep; cp; rp; corr } } -| EOF { assert false } - -correctif: - ENTREESC - ec = list(variable_and_value) - CONTROLESC - cc = list(variable_and_value) - RESULTATSC - rc = list(variable_and_value) { (ec, cc, rc) } - - -name: -| n = NAME { n } -| n = SYMBOL { n } - -fip: - FIP SLASH option(SYMBOL) { } - -variable_and_value: -| var = SYMBOL SLASH value = INTEGER { (var, I (int_of_string value), mk_position $sloc) } -| var = SYMBOL SLASH value = FLOAT { (var, F (float_of_string value), mk_position $sloc) }