From 576d7139bae24b8935d90e8f1bd2fcd1f5b62465 Mon Sep 17 00:00:00 2001 From: Alexandre Doussot Date: Tue, 16 Sep 2025 14:04:00 +0200 Subject: [PATCH 01/14] Split Cli into Cli (for argument parsing) and Config --- ir-calcul | 2 +- irj_checker.opam | 4 +- mlang.opam | 2 +- src/mlang/backend_compilers/bir_to_dgfip_c.ml | 1 + .../backend_compilers/bir_to_dgfip_c.mli | 2 +- src/mlang/backend_compilers/decoupledExpr.ml | 1 + src/mlang/backend_compilers/decoupledExpr.mli | 7 +- .../backend_compilers/dgfip_compir_files.ml | 4 +- .../backend_compilers/dgfip_gen_files.ml | 10 +- src/mlang/driver.ml | 76 ++++---- src/mlang/m_frontend/expander.ml | 2 +- src/mlang/m_frontend/validator.ml | 2 +- src/mlang/m_ir/mir_interpreter.ml | 12 +- src/mlang/m_ir/mir_interpreter.mli | 10 +- src/mlang/m_ir/mir_roundops.ml | 6 +- src/mlang/test_framework/test_interpreter.ml | 56 +++--- src/mlang/test_framework/test_interpreter.mli | 7 +- src/mlang/utils/cli.ml | 119 +----------- src/mlang/utils/cli.mli | 117 +----------- src/mlang/utils/config.ml | 174 ++++++++++++++++++ src/mlang/utils/config.mli | 146 +++++++++++++++ src/mlang/utils/dgfip_options.ml | 119 +++--------- src/mlang/utils/dgfip_options.mli | 27 +++ 23 files changed, 488 insertions(+), 418 deletions(-) create mode 100644 src/mlang/utils/config.ml create mode 100644 src/mlang/utils/config.mli create mode 100644 src/mlang/utils/dgfip_options.mli diff --git a/ir-calcul b/ir-calcul index 7af2b787a..f521842dc 160000 --- a/ir-calcul +++ b/ir-calcul @@ -1 +1 @@ -Subproject commit 7af2b787ac8aba998c5da59b6f1e7cc76320227c +Subproject commit f521842dcf83dea33df43f1068c13d24b90b9bd9 diff --git a/irj_checker.opam b/irj_checker.opam index a5f3c43f2..0d25f719a 100644 --- a/irj_checker.opam +++ b/irj_checker.opam @@ -1,6 +1,6 @@ # This file is generated by dune, edit dune-project instead opam-version: "2.0" -version: "1.1.0" +version: "%%VERSION%%" synopsis: "IRJ test validation tool" description: "This standalone module performs a syntactic validation of the DGFiP IRJ test format" @@ -10,7 +10,7 @@ license: "GPL-3.0-or-later" homepage: "https://github.com/MLanguage/mlang" bug-reports: "https://github.com/MLanguage/mlang/issues" depends: [ - "ocaml" {>= "4.13.0"} + "ocaml" {>= "4.11.2"} "dune" {build} "odoc" {>= "1.5.3"} "ocamlformat" {= "0.24.1"} diff --git a/mlang.opam b/mlang.opam index af79511af..4df20cb44 100644 --- a/mlang.opam +++ b/mlang.opam @@ -1,6 +1,6 @@ # This file is generated by dune, edit dune-project instead opam-version: "2.0" -version: "1.1.0" +version: "%%VERSION%%" synopsis: "Compiler for DGFiP's M language" description: """ The Direction Générale des Finances Publiques (DGFiP) diff --git a/src/mlang/backend_compilers/bir_to_dgfip_c.ml b/src/mlang/backend_compilers/bir_to_dgfip_c.ml index ba411932a..2b5ccbda8 100644 --- a/src/mlang/backend_compilers/bir_to_dgfip_c.ml +++ b/src/mlang/backend_compilers/bir_to_dgfip_c.ml @@ -16,6 +16,7 @@ module D = DecoupledExpr module VID = Dgfip_varid +module Dgfip_options = Config.Dgfip_options let str_escape str = let l = String.length str in diff --git a/src/mlang/backend_compilers/bir_to_dgfip_c.mli b/src/mlang/backend_compilers/bir_to_dgfip_c.mli index 6a6fa57d6..d543309f2 100644 --- a/src/mlang/backend_compilers/bir_to_dgfip_c.mli +++ b/src/mlang/backend_compilers/bir_to_dgfip_c.mli @@ -21,4 +21,4 @@ of the output, is built in {!DecoupledExpr}. *) val generate_c_program : - Dgfip_options.flags -> Mir.program -> (* filename *) string -> unit + Config.Dgfip_options.flags -> Mir.program -> (* filename *) string -> unit diff --git a/src/mlang/backend_compilers/decoupledExpr.ml b/src/mlang/backend_compilers/decoupledExpr.ml index 7cbcfaa9c..af8b26fd5 100644 --- a/src/mlang/backend_compilers/decoupledExpr.ml +++ b/src/mlang/backend_compilers/decoupledExpr.ml @@ -1,4 +1,5 @@ module VID = Dgfip_varid +module Dgfip_options = Config.Dgfip_options let generate_variable ?(def_flag = false) ?(trace_flag = false) (m_sp_opt : Com.var_space) (var : Com.Var.t) : string = diff --git a/src/mlang/backend_compilers/decoupledExpr.mli b/src/mlang/backend_compilers/decoupledExpr.mli index 166ffcd26..6313fbc51 100644 --- a/src/mlang/backend_compilers/decoupledExpr.mli +++ b/src/mlang/backend_compilers/decoupledExpr.mli @@ -151,7 +151,10 @@ val build_expression : val format_local_declarations : Format.formatter -> local_decls -> unit val format_assign : - Dgfip_options.flags -> string -> Format.formatter -> t -> unit + Config.Dgfip_options.flags -> string -> Format.formatter -> t -> unit val format_set_vars : - Dgfip_options.flags -> Format.formatter -> (dflag * string * t) list -> unit + Config.Dgfip_options.flags -> + Format.formatter -> + (dflag * string * t) list -> + unit diff --git a/src/mlang/backend_compilers/dgfip_compir_files.ml b/src/mlang/backend_compilers/dgfip_compir_files.ml index 0967de8bf..4b0212234 100644 --- a/src/mlang/backend_compilers/dgfip_compir_files.ml +++ b/src/mlang/backend_compilers/dgfip_compir_files.ml @@ -14,8 +14,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *) +module Dgfip_options = Config.Dgfip_options + let open_file filename = - let folder = Filename.dirname !Cli.output_file in + let folder = Filename.dirname !Config.output_file in let oc = open_out (Filename.concat folder filename) in let fmt = Format.formatter_of_out_channel oc in (oc, fmt) diff --git a/src/mlang/backend_compilers/dgfip_gen_files.ml b/src/mlang/backend_compilers/dgfip_gen_files.ml index 77a616149..84e2958a0 100644 --- a/src/mlang/backend_compilers/dgfip_gen_files.ml +++ b/src/mlang/backend_compilers/dgfip_gen_files.ml @@ -14,8 +14,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *) +module Dgfip_options = Config.Dgfip_options + let open_file filename = - let folder = Filename.dirname !Cli.output_file in + let folder = Filename.dirname !Config.output_file in let oc = open_out (Filename.concat folder filename) in let fmt = Format.formatter_of_out_channel oc in (oc, fmt) @@ -270,7 +272,7 @@ typedef struct S_varinfo_map { attrs let is_valid_app apps = - StrMap.exists (fun app _ -> List.mem app !Cli.application_names) apps + StrMap.exists (fun app _ -> List.mem app !Config.application_names) apps let gen_erreurs_c fmt flags (cprog : Mir.program) = Pp.fpr fmt {|/****** LICENCE CECIL *****/ @@ -347,7 +349,7 @@ let gen_conf_h fmt (cprog : Mir.program) flags = FLG_TRACE_IRDATA\n"; *) if flags.flg_debug then Pp.fpr fmt "#define FLG_DEBUG\n"; Pp.fpr fmt "#define NB_DEBUG_C %d\n" flags.nb_debug_c; - Pp.fpr fmt "#define EPSILON %f\n" !Cli.comparison_error_margin; + Pp.fpr fmt "#define EPSILON %f\n" !Config.comparison_error_margin; let count loc = StrMap.fold (fun _ var nb -> @@ -593,7 +595,7 @@ extern void free_erreur(); #define min(a,b) (((a) <= (b)) ? (a) : (b)) #define max(a,b) (((a) >= (b)) ? (a) : (b)) |}; - Pp.fpr fmt "#define EPSILON %f" !Cli.comparison_error_margin; + Pp.fpr fmt "#define EPSILON %f" !Config.comparison_error_margin; Pp.fpr fmt {| #define GT_E(a,b) ((a) > (b) + EPSILON) diff --git a/src/mlang/driver.ml b/src/mlang/driver.ml index 39eeda92f..97626de9b 100644 --- a/src/mlang/driver.ml +++ b/src/mlang/driver.ml @@ -19,7 +19,7 @@ open Mlexer exception Exit -let process_dgfip_options (backend : Cli.backend) +let process_dgfip_options (backend : Config.backend) ~(application_names : string list) (dgfip_options : string list option) = match backend with | Dgfip_c -> begin @@ -38,13 +38,14 @@ let process_dgfip_options (backend : Cli.backend) | Some flags -> flags end end - | UnknownBackend -> Dgfip_options.default_flags + | UnknownBackend -> Config.Dgfip_options.default_flags (* The legacy compiler plays a nasty trick on us, that we have to reproduce: rule 1 is modified to add assignments to APPLI_XXX variables according to the target application (OCEANS, BATCH and ILIAD). *) -let patch_rule_1 (backend : Cli.backend) (dgfip_flags : Dgfip_options.flags) - (program : Mast.program) : Mast.program = +let patch_rule_1 (backend : Config.backend) + (dgfip_flags : Config.Dgfip_options.flags) (program : Mast.program) : + Mast.program = let open Mast in let var_exists name = List.exists @@ -116,7 +117,7 @@ let parse () = in let parse_m_dgfip m_program = - if !Cli.without_dgfip_m then m_program + if !Config.without_dgfip_m then m_program else let parse_internal str = let filebuf = Lexing.from_string str in @@ -135,14 +136,14 @@ let parse () = in (*FIXME: use a fold here *) let prog = - List.map parse_file_progress @@ Cli.get_files !Cli.source_files + List.map parse_file_progress @@ Config.get_files !Config.source_files in List.rev prog @ m_program in let m_program = [] |> parse_m_dgfip |> parse_m_files |> List.rev - |> patch_rule_1 !Cli.backend !Cli.dgfip_flags + |> patch_rule_1 !Config.backend !Config.dgfip_flags in finish "completed!"; m_program @@ -158,35 +159,35 @@ let set_opts (files : string list) (application_names : string list) (run_test : string option) (mpp_function : string) (optimize_unsafe_float : bool) (precision : string option) (roundops : string option) (comparison_error_margin : float option) - (income_year : int option) (m_clean_calls : bool) + (income_year : int) (m_clean_calls : bool) (dgfip_options : string list option) = let value_sort = let precision = Option.get precision in - if precision = "double" then Cli.RegularFloat + if precision = "double" then Config.RegularFloat else let mpfr_regex = Re.Pcre.regexp "^mpfr(\\d+)$" in if Re.Pcre.pmatch ~rex:mpfr_regex precision then let mpfr_prec = Re.Pcre.get_substring (Re.Pcre.exec ~rex:mpfr_regex precision) 1 in - Cli.MPFR (int_of_string mpfr_prec) - else if precision = "interval" then Cli.Interval + Config.MPFR (int_of_string mpfr_prec) + else if precision = "interval" then Config.Interval else let bigint_regex = Re.Pcre.regexp "^fixed(\\d+)$" in if Re.Pcre.pmatch ~rex:bigint_regex precision then let fixpoint_prec = Re.Pcre.get_substring (Re.Pcre.exec ~rex:bigint_regex precision) 1 in - Cli.BigInt (int_of_string fixpoint_prec) - else if precision = "mpq" then Cli.Rational + Config.BigInt (int_of_string fixpoint_prec) + else if precision = "mpq" then Config.Rational else Errors.raise_error (Format.asprintf "Unkown precision option: %s" precision) in let round_ops = match roundops with - | Some "default" -> Cli.RODefault - | Some "multi" -> Cli.ROMulti + | Some "default" -> Config.RODefault + | Some "multi" -> Config.ROMulti | Some roundops -> let mf_regex = Re.Pcre.regexp "^mainframe(\\d+)$" in if Re.Pcre.pmatch ~rex:mf_regex roundops then @@ -194,7 +195,7 @@ let set_opts (files : string list) (application_names : string list) Re.Pcre.get_substring (Re.Pcre.exec ~rex:mf_regex roundops) 1 in match int_of_string mf_long_size with - | (32 | 64) as sz -> Cli.ROMainframe sz + | (32 | 64) as sz -> Config.ROMainframe sz | _ -> Errors.raise_error (Format.asprintf "Invalid long size for mainframe: %s" @@ -205,23 +206,23 @@ let set_opts (files : string list) (application_names : string list) | None -> Errors.raise_error @@ Format.asprintf "Unspecified roundops@." in let backend = - match backend with Some "dgfip_c" -> Cli.Dgfip_c | _ -> UnknownBackend + match backend with Some "dgfip_c" -> Config.Dgfip_c | _ -> UnknownBackend in let execution_mode = match (run_tests, run_test) with - | Some s, _ -> Cli.MultipleTests s - | None, Some s -> Cli.SingleTest s - | None, None -> Cli.Extraction + | Some s, _ -> Config.MultipleTests s + | None, Some s -> Config.SingleTest s + | None, None -> Config.Extraction in let files = match List.length files with | 0 -> Errors.raise_error "please provide at least one M source file" - | _ -> Cli.NonEmpty files + | _ -> Config.NonEmpty files in let dgfip_flags = - process_dgfip_options backend ~application_names dgfip_options + process_dgfip_options !Config.backend ~application_names dgfip_options in - Cli.set_all_arg_refs files application_names without_dgfip_m debug + Config.set_all_arg_refs files application_names without_dgfip_m debug var_info_debug display_time dep_graph_file print_cycles output optimize_unsafe_float m_clean_calls comparison_error_margin income_year value_sort round_ops backend dgfip_test_filter mpp_function dgfip_flags @@ -229,32 +230,29 @@ let set_opts (files : string list) (application_names : string list) let run_single_test m_program test = Mir_interpreter.repl_debug := true; - ignore - (Test_interpreter.check_one_test m_program test !Cli.value_sort - !Cli.round_ops); - Test_interpreter.check_one_test m_program test !Cli.value_sort !Cli.round_ops; + Test_interpreter.check_one_test m_program test !Config.value_sort !Config.round_ops; Cli.result_print "Test passed!" let run_multiple_tests m_program tests = let filter_function = - match !Cli.dgfip_test_filter with + match !Config.dgfip_test_filter with | false -> fun _ -> true | true -> ( fun x -> match x.[0] with 'A' .. 'Z' -> true | _ -> false) in - Test_interpreter.check_all_tests m_program tests !Cli.value_sort - !Cli.round_ops filter_function + Test_interpreter.check_all_tests m_program tests !Config.value_sort + !Config.round_ops filter_function let extract m_program = Cli.debug_print "Extracting the desired function from the whole program..."; - match !Cli.backend with - | Cli.Dgfip_c -> + match !Config.backend with + | Config.Dgfip_c -> Cli.debug_print "Compiling the codebase to DGFiP C..."; - if !Cli.output_file = "" then + if !Config.output_file = "" then Errors.raise_error "an output file must be defined with --output"; - Dgfip_gen_files.generate_auxiliary_files !Cli.dgfip_flags m_program; - Bir_to_dgfip_c.generate_c_program !Cli.dgfip_flags m_program - !Cli.output_file; - Cli.debug_print "Result written to %s" !Cli.output_file + Dgfip_gen_files.generate_auxiliary_files !Config.dgfip_flags m_program; + Bir_to_dgfip_c.generate_c_program !Config.dgfip_flags m_program + !Config.output_file; + Cli.debug_print "Result written to %s" !Config.output_file | UnknownBackend -> Errors.raise_error "No backend specified!" let driver () = @@ -263,11 +261,11 @@ let driver () = let m_program = parse () in Cli.debug_print "Elaborating..."; let m_program = Expander.proceed m_program in - let m_program = Validator.proceed !Cli.mpp_function m_program in + let m_program = Validator.proceed !Config.mpp_function m_program in let m_program = Mast_to_mir.translate m_program in let m_program = Mir.expand_functions m_program in Cli.debug_print "Creating combined program suitable for execution..."; - match !Cli.execution_mode with + match !Config.execution_mode with | SingleTest test -> run_single_test m_program test | MultipleTests tests -> run_multiple_tests m_program tests | Extraction -> extract m_program diff --git a/src/mlang/m_frontend/expander.ml b/src/mlang/m_frontend/expander.ml index 624ae3795..15629742f 100644 --- a/src/mlang/m_frontend/expander.ml +++ b/src/mlang/m_frontend/expander.ml @@ -265,7 +265,7 @@ let elim_unselected_apps (p : Mast.program) : Mast.program = (apps_env, []) source_file in (apps_env, List.rev prog_file :: prog)) - (empty_apps_env !Cli.application_names, []) + (empty_apps_env !Config.application_names, []) p in check_apps_on_cmdline apps_env; diff --git a/src/mlang/m_frontend/validator.ml b/src/mlang/m_frontend/validator.ml index ccee6b821..873236dca 100644 --- a/src/mlang/m_frontend/validator.ml +++ b/src/mlang/m_frontend/validator.ml @@ -583,7 +583,7 @@ let safe_prefix (p : Mast.program) : string = let empty_program (p : Mast.program) main_target = let prog_app = let fold s a = StrMap.add a Pos.none s in - List.fold_left fold StrMap.empty !Cli.application_names + List.fold_left fold StrMap.empty !Config.application_names in { prog_prefix = safe_prefix p; diff --git a/src/mlang/m_ir/mir_interpreter.ml b/src/mlang/m_ir/mir_interpreter.ml index e2c1b01bc..9b5d54db2 100644 --- a/src/mlang/m_ir/mir_interpreter.ml +++ b/src/mlang/m_ir/mir_interpreter.ml @@ -335,7 +335,7 @@ struct let bool_of_real (f : N.t) : bool = not N.(f =. zero ()) let compare_numbers op i1 i2 = - let epsilon = N.of_float !Cli.comparison_error_margin in + let epsilon = N.of_float !Config.comparison_error_margin in let open Com in match op with | Gt -> N.(i1 >. i2 +. epsilon) @@ -1259,7 +1259,8 @@ module RatMfInterp = (Mir_number.RationalNumber) (Mir_roundops.MainframeRoundOps (MainframeLongSize)) -let get_interp (sort : Cli.value_sort) (roundops : Cli.round_ops) : (module S) = +let get_interp (sort : Config.value_sort) (roundops : Config.round_ops) : + (module S) = match (sort, roundops) with | RegularFloat, RODefault -> (module FloatDefInterp) | RegularFloat, ROMulti -> (module FloatMultInterp) @@ -1277,7 +1278,8 @@ let get_interp (sort : Cli.value_sort) (roundops : Cli.round_ops) : (module S) = | Rational, ROMulti -> (module RatMultInterp) | Rational, ROMainframe _ -> (module RatMfInterp) -let prepare_interp (sort : Cli.value_sort) (roundops : Cli.round_ops) : unit = +let prepare_interp (sort : Config.value_sort) (roundops : Config.round_ops) : + unit = begin match sort with | MPFR prec -> Mpfr.set_default_prec prec @@ -1298,7 +1300,7 @@ let prepare_interp (sort : Cli.value_sort) (roundops : Cli.round_ops) : unit = let evaluate_program (p : Mir.program) (inputs : Com.literal Com.Var.Map.t) (events : (Com.literal, Com.Var.t) Com.event_value StrMap.t list) - (sort : Cli.value_sort) (roundops : Cli.round_ops) : + (sort : Config.value_sort) (roundops : Config.round_ops) : Com.literal Com.Var.Map.t * Com.Error.Set.t = prepare_interp sort roundops; let module Interp = (val get_interp sort roundops : S) in @@ -1333,6 +1335,6 @@ let evaluate_program (p : Mir.program) (inputs : Com.literal Com.Var.Map.t) (varMap, anoSet) let evaluate_expr (p : Mir.program) (e : Mir.expression Pos.marked) - (sort : Cli.value_sort) (roundops : Cli.round_ops) : Com.literal = + (sort : Config.value_sort) (roundops : Config.round_ops) : Com.literal = let module Interp = (val get_interp sort roundops : S) in Interp.value_to_literal (Interp.evaluate_expr (Interp.empty_ctx p) e) diff --git a/src/mlang/m_ir/mir_interpreter.mli b/src/mlang/m_ir/mir_interpreter.mli index c9225427f..36c7e3c79 100644 --- a/src/mlang/m_ir/mir_interpreter.mli +++ b/src/mlang/m_ir/mir_interpreter.mli @@ -172,21 +172,21 @@ module RatMfInterp : S with type custom_float = Mir_number.RationalNumber.t (** {1 Generic interpretation API}*) -val get_interp : Cli.value_sort -> Cli.round_ops -> (module S) +val get_interp : Config.value_sort -> Config.round_ops -> (module S) val evaluate_program : Mir.program -> Com.literal Com.Var.Map.t -> (Com.literal, Com.Var.t) Com.event_value StrMap.t list -> - Cli.value_sort -> - Cli.round_ops -> + Config.value_sort -> + Config.round_ops -> Com.literal Com.Var.Map.t * Com.Error.Set.t (** Main interpreter function *) val evaluate_expr : Mir.program -> Mir.expression Pos.marked -> - Cli.value_sort -> - Cli.round_ops -> + Config.value_sort -> + Config.round_ops -> Com.literal (** Interprets only an expression *) diff --git a/src/mlang/m_ir/mir_roundops.ml b/src/mlang/m_ir/mir_roundops.ml index f39fad3ca..f6577e50e 100644 --- a/src/mlang/m_ir/mir_roundops.ml +++ b/src/mlang/m_ir/mir_roundops.ml @@ -29,7 +29,7 @@ module DefaultRoundOps (N : Mir_number.NumberInterface) : RoundOpsInterface with type t = N.t = struct type t = N.t - let epsilon = !Cli.comparison_error_margin + let epsilon = !Config.comparison_error_margin let truncatef (x : N.t) : N.t = N.floor N.(x +. N.of_float epsilon) @@ -45,7 +45,7 @@ module MultiRoundOps (N : Mir_number.NumberInterface) : RoundOpsInterface with type t = N.t = struct type t = N.t - let epsilon = !Cli.comparison_error_margin + let epsilon = !Config.comparison_error_margin let truncatef (x : N.t) : N.t = N.floor N.(x +. N.of_float epsilon) @@ -60,7 +60,7 @@ end) (N : Mir_number.NumberInterface) : RoundOpsInterface with type t = N.t = struct type t = N.t - let epsilon = !Cli.comparison_error_margin + let epsilon = !Config.comparison_error_margin let floor_g (x : N.t) : N.t = if N.abs x <= N.of_int !L.max_long then N.floor x else x diff --git a/src/mlang/test_framework/test_interpreter.ml b/src/mlang/test_framework/test_interpreter.ml index dd49135ca..632ca918c 100644 --- a/src/mlang/test_framework/test_interpreter.ml +++ b/src/mlang/test_framework/test_interpreter.ml @@ -36,7 +36,7 @@ let to_MIR_function_and_inputs (program : Mir.program) (t : Irj_ast.irj_file) : let map_init = try let ancsded = find_var_of_name program (Pos.without "V_ANCSDED") in - let ancsded_val = Com.Float (float_of_int (!Cli.income_year + 1)) in + let ancsded_val = Com.Float (float_of_int (!Config.income_year + 1)) in Com.Var.Map.one ancsded ancsded_val with _ -> Com.Var.Map.empty in @@ -136,7 +136,7 @@ let to_MIR_function_and_inputs (program : Mir.program) (t : Irj_ast.irj_file) : exception InterpError of int let check_test (program : Mir.program) (test_name : string) - (value_sort : Cli.value_sort) (round_ops : Cli.round_ops) : unit = + (value_sort : Config.value_sort) (round_ops : Config.round_ops) : unit = let check_vars exp vars = let test_error_margin = 0.01 in let fold vname f nb = @@ -171,10 +171,10 @@ let check_test (program : Mir.program) (test_name : string) StrSet.iter (Cli.error_print "KO | unexpected error: %s") unexAnos; StrSet.cardinal missAnos + StrSet.cardinal unexAnos in - let dbg_warning = !Cli.warning_flag in - let dbg_time = !Cli.display_time in - Cli.warning_flag := false; - Cli.display_time := false; + let dbg_warning = !Config.warning_flag in + let dbg_time = !Config.display_time in + Config.warning_flag := false; + Config.display_time := false; Cli.debug_print "Parsing %s..." test_name; let t = Irj_file.parse_file test_name in Cli.debug_print "Running test %s..." t.nom; @@ -201,13 +201,13 @@ let check_test (program : Mir.program) (test_name : string) raise (InterpError nbErrs)) in check insts; - Cli.warning_flag := dbg_warning; - Cli.display_time := dbg_time + Config.warning_flag := dbg_warning; + Config.display_time := dbg_time type process_acc = string list * int StrMap.t let check_all_tests (p : Mir.program) (test_dir : string) - (value_sort : Cli.value_sort) (round_ops : Cli.round_ops) + (value_sort : Config.value_sort) (round_ops : Config.round_ops) (filter_function : string -> bool) = let arr = Sys.readdir test_dir in let arr = @@ -220,20 +220,20 @@ let check_all_tests (p : Mir.program) (test_dir : string) Mir_interpreter.exit_on_rte := false; (* sort by increasing size, hoping that small files = simple tests *) Array.sort compare arr; - let dbg_warning = !Cli.warning_flag in - let dbg_time = !Cli.display_time in - Cli.warning_flag := false; - Cli.display_time := false; - (* let _, finish = Cli.create_progress_bar "Testing files" in*) + let dbg_warning = !Config.warning_flag in + let dbg_time = !Config.display_time in + Config.warning_flag := false; + Config.display_time := false; + (* let _, finish = Config.create_progress_bar "Testing files" in*) let process (name : string) ((successes, failures) : process_acc) : process_acc = let module Interp = (val Mir_interpreter.get_interp value_sort round_ops : Mir_interpreter.S) in try - Cli.debug_flag := false; + Config.debug_flag := false; check_test p (test_dir ^ name) value_sort round_ops; - Cli.debug_flag := true; + Config.debug_flag := true; Cli.result_print "%s" name; (name :: successes, failures) with @@ -267,8 +267,8 @@ let check_all_tests (p : Mir.program) (test_dir : string) *) in (* finish "done!"; *) - Cli.warning_flag := dbg_warning; - Cli.display_time := dbg_time; + Config.warning_flag := dbg_warning; + Config.display_time := dbg_time; Cli.result_print "Test results: %d successes" (List.length s); if StrMap.cardinal f = 0 then Cli.result_print "No failures!" @@ -279,22 +279,22 @@ let check_all_tests (p : Mir.program) (test_dir : string) f) let check_one_test (p : Mir.program) (name : string) - (value_sort : Cli.value_sort) (round_ops : Cli.round_ops) = + (value_sort : Config.value_sort) (round_ops : Config.round_ops) = Mir_interpreter.exit_on_rte := false; (* sort by increasing size, hoping that small files = simple tests *) - let dbg_warning = !Cli.warning_flag in - let dbg_time = !Cli.display_time in - Cli.warning_flag := false; - Cli.display_time := false; - (* let _, finish = Cli.create_progress_bar "Testing files" in*) + let dbg_warning = !Config.warning_flag in + let dbg_time = !Config.display_time in + Config.warning_flag := false; + Config.display_time := false; + (* let _, finish = Config.create_progress_bar "Testing files" in*) let is_ok = let module Interp = (val Mir_interpreter.get_interp value_sort round_ops : Mir_interpreter.S) in try - Cli.debug_flag := false; + Config.debug_flag := false; check_test p name value_sort round_ops; - Cli.debug_flag := true; + Config.debug_flag := true; Cli.result_print "%s" name; None with @@ -320,8 +320,8 @@ let check_one_test (p : Mir.program) (name : string) raise e in (* finish "done!"; *) - Cli.warning_flag := dbg_warning; - Cli.display_time := dbg_time; + Config.warning_flag := dbg_warning; + Config.display_time := dbg_time; match is_ok with | None -> Cli.result_print "No failure!" | Some 0 -> Cli.error_print "Unexpected failure" diff --git a/src/mlang/test_framework/test_interpreter.mli b/src/mlang/test_framework/test_interpreter.mli index 5fe88b654..31531b0e6 100644 --- a/src/mlang/test_framework/test_interpreter.mli +++ b/src/mlang/test_framework/test_interpreter.mli @@ -12,17 +12,16 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *) - val check_all_tests : Mir.program -> string -> - Cli.value_sort -> - Cli.round_ops -> + Config.value_sort -> + Config.round_ops -> (string -> bool) -> unit (** [check_all_tests p folder vs ro filter] Executes [p] with all tests in [folder] whose name satisfy [filter]. *) val check_one_test : - Mir.program -> string -> Cli.value_sort -> Cli.round_ops -> unit + Mir.program -> string -> Config.value_sort -> Config.round_ops -> unit (** Same as [check_all_tests], but for one test. *) diff --git a/src/mlang/utils/cli.ml b/src/mlang/utils/cli.ml index e84b321b0..2970f891b 100644 --- a/src/mlang/utils/cli.ml +++ b/src/mlang/utils/cli.ml @@ -23,6 +23,10 @@ (** The command line interface is declared using {!module Cmdliner} *) open Cmdliner +open Config +module Cmdliner = Cmdliner +module Term = Cmdliner.Term +module ANSITerminal = ANSITerminal let files = Arg.( @@ -160,7 +164,7 @@ let comparison_error_margin_cli = let income_year_cli = Arg.( value - & opt (some int) None + & opt (int) (1900 + (Unix.localtime (Unix.time())).Unix.tm_year - 1) & info [ "income-year" ] ~docv:"INCOME_YEAR" ~doc:"Set the year of the income.") @@ -232,115 +236,6 @@ let info = | Some v -> Build_info.V1.Version.to_string v) ~doc ~exits ~man -type value_sort = - | RegularFloat - | MPFR of int (* bitsize of the floats *) - | BigInt of int (* precision of the fixed point *) - | Interval - | Rational - -type round_ops = RODefault | ROMulti | ROMainframe of int -(* size of type long, either 32 or 64 *) - -type backend = Dgfip_c | UnknownBackend - -type execution_mode = - | SingleTest of string - | MultipleTests of string - | Extraction - -type files = NonEmpty of string list - -let get_files = function NonEmpty l -> l - -(* This feels weird to put here, but by construction it should not happen.*) -let source_files : files ref = ref (NonEmpty []) - -let application_names : string list ref = ref [] - -let without_dgfip_m = ref false - -let dep_graph_file : string ref = ref "dep_graph.dot" - -let verify_flag = ref false - -let debug_flag = ref false - -let var_info_flag = ref false - -let var_info_debug = ref [] - -let warning_flag = ref true - -let no_print_cycles_flag = ref false - -let display_time = ref false - -let output_file = ref "" - -let optimize_unsafe_float = ref false - -let m_clean_calls = ref false - -let value_sort = ref RegularFloat - -let round_ops = ref RODefault - -let backend = ref UnknownBackend - -let dgfip_test_filter = ref false - -let mpp_function = ref "" - -let dgfip_flags = ref Dgfip_options.default_flags - -let execution_mode = ref Extraction - -(* Default value for the epsilon slack when comparing things in the - interpreter *) -let comparison_error_margin = ref 0.000001 - -let income_year = ref 0 - -let set_all_arg_refs (files_ : files) applications_ (without_dgfip_m_ : bool) - (debug_ : bool) (var_info_debug_ : string list) (display_time_ : bool) - (dep_graph_file_ : string) (no_print_cycles_ : bool) - (output_file_ : string option) (optimize_unsafe_float_ : bool) - (m_clean_calls_ : bool) (comparison_error_margin_ : float option) - (income_year_ : int option) (value_sort_ : value_sort) - (round_ops_ : round_ops) (backend_ : backend) (dgfip_test_filter_ : bool) - (mpp_function_ : string) (dgfip_flags_ : Dgfip_options.flags) - (execution_mode_ : execution_mode) = - source_files := files_; - application_names := applications_; - without_dgfip_m := without_dgfip_m_; - debug_flag := debug_; - var_info_debug := var_info_debug_; - var_info_flag := !var_info_debug <> []; - display_time := display_time_; - dep_graph_file := dep_graph_file_; - no_print_cycles_flag := no_print_cycles_; - optimize_unsafe_float := optimize_unsafe_float_; - m_clean_calls := m_clean_calls_; - execution_mode := execution_mode_; - (income_year := - match income_year_ with - | Some y -> y - | None -> 1900 + (Unix.localtime (Unix.time ())).Unix.tm_year - 1); - value_sort := value_sort_; - round_ops := round_ops_; - backend := backend_; - dgfip_test_filter := dgfip_test_filter_; - mpp_function := mpp_function_; - dgfip_flags := dgfip_flags_; - match output_file_ with - | None -> () - | Some o -> ( - output_file := o; - match comparison_error_margin_ with - | None -> () - | Some m -> comparison_error_margin := m) - (**{1 Terminal formatting}*) let concat_with_line_depending_prefix_and_suffix (prefix : int -> string) @@ -431,13 +326,13 @@ let debug_print ?(endline = "\n") kont = (fun str -> Format.printf "%a%s%s@?" (fun _ -> debug_marker) - !display_time str endline) + !Config.display_time str endline) kont else Format.ifprintf Format.std_formatter kont let var_info_print kont = ANSITerminal.erase ANSITerminal.Eol; - if !var_info_flag then + if !Config.var_info_flag then Format.kasprintf (fun str -> Format.printf "%a%s@." (fun _ -> var_info_marker) () str) kont diff --git a/src/mlang/utils/cli.mli b/src/mlang/utils/cli.mli index 2ca786c71..259cdb772 100644 --- a/src/mlang/utils/cli.mli +++ b/src/mlang/utils/cli.mli @@ -37,7 +37,7 @@ val mlang_t : string option -> string option -> float option -> - int option -> + int -> bool -> string list option -> 'a) -> @@ -47,121 +47,6 @@ val mlang_t : val info : Cmdliner.Cmd.info (** Command-line man page for --help *) -(**{2 Flags and parameters}*) - -(** According on the [value_sort], a specific interpreter will be called with - the right kind of floating-point value *) -type value_sort = - | RegularFloat - | MPFR of int (** bitsize of the floats *) - | BigInt of int (** precision of the fixed point *) - | Interval - | Rational - -(** Rounding operations to use in the interpreter. They correspond to the - rounding operations used by the DGFiP calculator in different execution - contexts. - - - RODefault: rounding operations used in the PC/single-thread context - - ROMulti: rouding operations used in the PC/multi-thread context - - ROMainframe rounding operations used in the mainframe context *) -type round_ops = - | RODefault - | ROMulti - | ROMainframe of int (** size of type long, either 32 or 64 *) - -type backend = Dgfip_c | UnknownBackend - -type execution_mode = - | SingleTest of string - | MultipleTests of string - | Extraction - -type files = NonEmpty of string list - -val get_files : files -> string list - -val source_files : files ref -(** M source files to be compiled *) - -val application_names : string list ref - -val dep_graph_file : string ref -(** Prefix for debug graph output files *) - -val without_dgfip_m : bool ref - -val verify_flag : bool ref -(** Use Z3 to check if verif rules hold all the time *) - -val debug_flag : bool ref -(** Prints debug information *) - -val var_info_flag : bool ref -(** Print infomation about variables declared, defined ou used incorrectly *) - -val var_info_debug : string list ref -(** Prints even more information but only about some variables members of a list -*) - -val warning_flag : bool ref -(** Print warning info *) - -val no_print_cycles_flag : bool ref -(** Dump circular definitions of variables *) - -val display_time : bool ref -(** Displays timing information *) - -val output_file : string ref -(** Output file *) - -val optimize_unsafe_float : bool ref -(** Activate unsafe floating point optimizations *) - -val m_clean_calls : bool ref -(** Clean regular variables between M calls *) - -val comparison_error_margin : float ref - -val income_year : int ref - -val value_sort : value_sort ref - -val round_ops : round_ops ref - -val backend : backend ref - -val dgfip_test_filter : bool ref - -val mpp_function : string ref - -val dgfip_flags : Dgfip_options.flags ref - -val execution_mode : execution_mode ref - -val set_all_arg_refs : - (* files *) files -> - (* applications *) string list -> - (* without_dgfip_m *) bool -> - (* debug *) bool -> - (* var_info_debug *) string list -> - (* display_time *) bool -> - (* dbg_graph_file *) string -> - (* prints_cycles *) bool -> - (* output_file *) string option -> - (* optimize_unsafe_float *) bool -> - (* m_clean_call *) bool -> - (* comparison_error_margin*) float option -> - (* income_year *) int option -> - value_sort -> - round_ops -> - backend -> - (* dgfip_test_filter *) bool -> - (* mpp_function *) string -> - (* dgfip_flags *) Dgfip_options.flags -> - (* execution_mode *) execution_mode -> - unit val add_prefix_to_each_line : string -> (int -> string) -> string (** [add_prefix_to_each_line msg prefix] will print msg but each line with line diff --git a/src/mlang/utils/config.ml b/src/mlang/utils/config.ml new file mode 100644 index 000000000..036d98c68 --- /dev/null +++ b/src/mlang/utils/config.ml @@ -0,0 +1,174 @@ +module Dgfip_options = struct + type flags = { + (* -m *) annee_revenu : int; + (* -P *) flg_correctif : bool; + (* flg_correctif true by default, -P makes it false *) + (* -R *) flg_iliad : bool; + (* also implied by "iliad" in !Cli.application_names; disabled by -U *) + (* -R *) flg_pro : bool; + (* also implied by "pro" in !Cli.application_names; disabled by -U *) + (* -U *) flg_cfir : bool; + (* disabled by -R *) + (* -b *) flg_gcos : bool; + (* -b0 and -b1 ; disabled by -U and -R *) + (* -b *) flg_tri_ebcdic : bool; + (* -b1 only *) + (* -s *) flg_short : bool; + (* -r *) flg_register : bool; + (* -O *) flg_optim_min_max : bool; + (* -X *) flg_extraction : bool; + (* -D *) flg_genere_libelle_restituee : bool; + (* -S *) flg_controle_separe : bool; + (* -I *) flg_controle_immediat : bool; + (* unused *) + (* -o *) flg_overlays : bool; + (* -Z *) flg_colors : bool; + (* -L *) flg_ticket : bool; + (* -t *) flg_trace : bool; + (* -g *) flg_debug : bool; + (* also implied by -t *) + (* -k *) nb_debug_c : int; + (* -x *) + xflg : bool; + (* Flags to deal with in a particular way : -c compilation mode -l link + mode -v specify the variable file (tgv.m) -e specify the error file + (err.m) *) + (* Other flags, not used in makefiles -h dir_var_h -i flg_ident + -K flg_optim_cte -G flg_listing (+genere_cre = FALSE) -p + flag_phase -f flg_ench_init -E cvt_file -g flg_debug -a flg_api -T + flg_trace_irdata *) + } + + let default_flags = + { + annee_revenu = 1991; + flg_correctif = true; + flg_iliad = false; + flg_pro = false; + flg_cfir = false; + flg_gcos = false; + flg_tri_ebcdic = false; + flg_short = false; + flg_register = false; + flg_optim_min_max = false; + flg_extraction = false; + flg_genere_libelle_restituee = false; + flg_controle_separe = false; + flg_controle_immediat = false; + flg_overlays = false; + flg_colors = false; + flg_ticket = false; + flg_trace = false; + flg_debug = false; + nb_debug_c = 0; + xflg = false; + } +end + +type value_sort = + | RegularFloat + | MPFR of int (* bitsize of the floats *) + | BigInt of int (* precision of the fixed point *) + | Interval + | Rational + +type round_ops = RODefault | ROMulti | ROMainframe of int +(* size of type long, either 32 or 64 *) + +type backend = Dgfip_c | UnknownBackend + +type execution_mode = + | SingleTest of string + | MultipleTests of string + | Extraction + +type files = NonEmpty of string list + +(* Flags inherited from the old compiler *) + +let get_files = function NonEmpty l -> l + +(* This feels weird to put here, but by construction it should not happen.*) +let source_files : files ref = ref (NonEmpty []) + +let application_names : string list ref = ref [] + +let without_dgfip_m = ref false + +let dbg_graph_file : string ref = ref "dbg_graph.dot" + +let verify_flag = ref false + +let debug_flag = ref false + +let var_info_flag = ref false + +let var_info_debug = ref [] + +let warning_flag = ref true + +let no_print_cycles_flag = ref false + +let display_time = ref false + +let output_file = ref "" + +let optimize_unsafe_float = ref false + +let m_clean_calls = ref false + +let value_sort = ref RegularFloat + +let round_ops = ref RODefault + +let backend = ref UnknownBackend + +let dgfip_test_filter = ref false + +let mpp_function = ref "" + +let dgfip_flags = ref Dgfip_options.default_flags + +let execution_mode = ref Extraction + +(* Default value for the epsilon slack when comparing things in the + interpreter *) +let comparison_error_margin = ref 0.000001 + +let income_year = ref 0 + +let set_all_arg_refs (files_ : files) applications_ (without_dgfip_m_ : bool) + (debug_ : bool) (var_info_debug_ : string list) (display_time_ : bool) + (dbg_graph_file_ : string) (no_print_cycles_ : bool) + (output_file_ : string option) (optimize_unsafe_float_ : bool) + (m_clean_calls_ : bool) (comparison_error_margin_ : float option) + (income_year_ : int) (value_sort_ : value_sort) + (round_ops_ : round_ops) (backend_ : backend) (dgfip_test_filter_ : bool) + (mpp_function_ : string) (dgfip_flags_ : Dgfip_options.flags) + (execution_mode_ : execution_mode) = + source_files := files_; + application_names := applications_; + without_dgfip_m := without_dgfip_m_; + debug_flag := debug_; + var_info_debug := var_info_debug_; + var_info_flag := !var_info_debug <> []; + display_time := display_time_; + dbg_graph_file := dbg_graph_file_; + no_print_cycles_flag := no_print_cycles_; + optimize_unsafe_float := optimize_unsafe_float_; + m_clean_calls := m_clean_calls_; + execution_mode := execution_mode_; + income_year := income_year_; + value_sort := value_sort_; + round_ops := round_ops_; + backend := backend_; + dgfip_test_filter := dgfip_test_filter_; + mpp_function := mpp_function_; + dgfip_flags := dgfip_flags_; + match output_file_ with + | None -> () + | Some o -> ( + output_file := o; + match comparison_error_margin_ with + | None -> () + | Some m -> comparison_error_margin := m) diff --git a/src/mlang/utils/config.mli b/src/mlang/utils/config.mli new file mode 100644 index 000000000..fd6b29209 --- /dev/null +++ b/src/mlang/utils/config.mli @@ -0,0 +1,146 @@ +(**{2 Flags and parameters}*) + +(** Special dgfip options for the compirateur *) + +module Dgfip_options : sig + type flags = { + annee_revenu : int; + flg_correctif : bool; + flg_iliad : bool; + flg_pro : bool; + flg_cfir : bool; + flg_gcos : bool; + flg_tri_ebcdic : bool; + flg_short : bool; + flg_register : bool; + flg_optim_min_max : bool; + flg_extraction : bool; + flg_genere_libelle_restituee : bool; + flg_controle_separe : bool; + flg_controle_immediat : bool; + flg_overlays : bool; + flg_colors : bool; + flg_ticket : bool; + flg_trace : bool; + flg_debug : bool; + nb_debug_c : int; + xflg : bool; + } + + val default_flags : flags +end + +(** According on the [value_sort], a specific interpreter will be called with + the right kind of floating-point value *) +type value_sort = + | RegularFloat + | MPFR of int (** bitsize of the floats *) + | BigInt of int (** precision of the fixed point *) + | Interval + | Rational + +(** Rounding operations to use in the interpreter. They correspond to the + rounding operations used by the DGFiP calculator in different execution + contexts. + + - RODefault: rounding operations used in the PC/single-thread context + - ROMulti: rouding operations used in the PC/multi-thread context + - ROMainframe rounding operations used in the mainframe context *) +type round_ops = + | RODefault + | ROMulti + | ROMainframe of int (** size of type long, either 32 or 64 *) + +type backend = Dgfip_c | UnknownBackend + +type execution_mode = + | SingleTest of string + | MultipleTests of string + | Extraction + +type files = NonEmpty of string list + +val get_files : files -> string list + +val source_files : files ref +(** M source files to be compiled *) + +val application_names : string list ref + +val dbg_graph_file : string ref +(** Prefix for debug graph output files *) + +val without_dgfip_m : bool ref + +val verify_flag : bool ref +(** Use Z3 to check if verif rules hold all the time *) + +val debug_flag : bool ref +(** Prints debug information *) + +val var_info_flag : bool ref +(** Print infomation about variables declared, defined ou used incorrectly *) + +val var_info_debug : string list ref +(** Prints even more information but only about some variables members of a list +*) + +val warning_flag : bool ref +(** Print warning info *) + +val no_print_cycles_flag : bool ref +(** Dump circular definitions of variables *) + +val display_time : bool ref +(** Displays timing information *) + +val output_file : string ref +(** Output file *) + +val optimize_unsafe_float : bool ref +(** Activate unsafe floating point optimizations *) + +val m_clean_calls : bool ref +(** Clean regular variables between M calls *) + +val comparison_error_margin : float ref + +val income_year : int ref + +val value_sort : value_sort ref + +val round_ops : round_ops ref + +val backend : backend ref + +val dgfip_test_filter : bool ref + +val mpp_function : string ref + +val dgfip_flags : Dgfip_options.flags ref + +val execution_mode : execution_mode ref + +val set_all_arg_refs : + (* files *) files -> + (* applications *) string list -> + (* without_dgfip_m *) bool -> + (* debug *) bool -> + (* var_info_debug *) string list -> + (* display_time *) bool -> + (* dbg_graph_file *) string -> + (* prints_cycles *) bool -> + (* output_file *) string option -> + (* optimize_unsafe_float *) bool -> + (* m_clean_call *) bool -> + (* comparison_error_margin*) float option -> + (* income_year *) int -> + value_sort -> + round_ops -> + backend -> + (* dgfip_test_filter *) bool -> + (* mpp_function *) string -> + (* dgfip_flags *) Dgfip_options.flags -> + (* execution_mode *) execution_mode -> + unit + diff --git a/src/mlang/utils/dgfip_options.ml b/src/mlang/utils/dgfip_options.ml index 9d5ffdd78..6e02ec4d1 100644 --- a/src/mlang/utils/dgfip_options.ml +++ b/src/mlang/utils/dgfip_options.ml @@ -97,106 +97,41 @@ let info = in Cmd.info "mlang --dgfip_options" ~doc ~man -(* Flags inherited from the old compiler *) -type flags = { - (* -m *) annee_revenu : int; - (* -P *) flg_correctif : bool; - (* flg_correctif true by default, -P makes it false *) - (* -R *) flg_iliad : bool; - (* also implied by "iliad" in !Cli.application_names; disabled by -U *) - (* -R *) flg_pro : bool; - (* also implied by "pro" in !Cli.application_names; disabled by -U *) - (* -U *) flg_cfir : bool; - (* disabled by -R *) - (* -b *) flg_gcos : bool; - (* -b0 and -b1 ; disabled by -U and -R *) - (* -b *) flg_tri_ebcdic : bool; - (* -b1 only *) - (* -s *) flg_short : bool; - (* -r *) flg_register : bool; - (* -O *) flg_optim_min_max : bool; - (* -X *) flg_extraction : bool; - (* -D *) flg_genere_libelle_restituee : bool; - (* -S *) flg_controle_separe : bool; - (* -I *) flg_controle_immediat : bool; - (* unused *) - (* -o *) flg_overlays : bool; - (* -Z *) flg_colors : bool; - (* -L *) flg_ticket : bool; - (* -t *) flg_trace : bool; - (* -g *) flg_debug : bool; - (* also implied by -t *) - (* -k *) nb_debug_c : int; - (* -x *) - xflg : bool; - (* Flags to deal with in a particular way : -c compilation mode -l link - mode -v specify the variable file (tgv.m) -e specify the error file - (err.m) *) - (* Other flags, not used in makefiles -h dir_var_h -i flg_ident - -K flg_optim_cte -G flg_listing (+genere_cre = FALSE) -p - flag_phase -f flg_ench_init -E cvt_file -g flg_debug -a flg_api -T - flg_trace_irdata *) -} - -let default_flags = - { - annee_revenu = 1991; - flg_correctif = true; - flg_iliad = false; - flg_pro = false; - flg_cfir = false; - flg_gcos = false; - flg_tri_ebcdic = false; - flg_short = false; - flg_register = false; - flg_optim_min_max = false; - flg_extraction = false; - flg_genere_libelle_restituee = false; - flg_controle_separe = false; - flg_controle_immediat = false; - flg_overlays = false; - flg_colors = false; - flg_ticket = false; - flg_trace = false; - flg_debug = false; - nb_debug_c = 0; - xflg = false; - } - let handler ~(application_names : string list) (income_year : int) (iliad_pro : bool) (cfir : bool) (batch : int option) (primitive_only : bool) (extraction : bool) (separate_controls : bool) (immediate_controls : bool) (overlays : bool) (optim_min_max : bool) (register : bool) (short : bool) (output_labels : bool) (debug : bool) (nb_debug_c : int) (trace : bool) (ticket : bool) (colored_output : bool) - (cross_references : bool) : flags = + (cross_references : bool) : Config.Dgfip_options.flags = let has_iliad = List.mem "iliad" application_names in let has_pro = List.mem "pro" application_names in - { - (* iliad, pro, (GP) *) - annee_revenu = income_year; - flg_correctif = not primitive_only; - flg_iliad = - ((iliad_pro && not cfir) || has_iliad) && not (Option.is_some batch); - flg_pro = (has_pro || iliad_pro) && not cfir; - flg_cfir = cfir && not iliad_pro; - flg_gcos = Option.is_some batch && (not iliad_pro) && not cfir; - flg_tri_ebcdic = (match batch with Some 1 -> true | _ -> false); - flg_short = short; - flg_register = register; - flg_optim_min_max = optim_min_max; - flg_extraction = extraction; - flg_genere_libelle_restituee = output_labels; - flg_controle_separe = separate_controls; - flg_controle_immediat = immediate_controls; - flg_overlays = overlays; - flg_colors = colored_output; - flg_ticket = ticket; - flg_trace = trace; - flg_debug = debug || trace; - nb_debug_c; - xflg = cross_references; - } + Config.Dgfip_options. + { + (* iliad, pro, (GP) *) + annee_revenu = income_year; + flg_correctif = not primitive_only; + flg_iliad = + ((iliad_pro && not cfir) || has_iliad) && not (Option.is_some batch); + flg_pro = (has_pro || iliad_pro) && not cfir; + flg_cfir = cfir && not iliad_pro; + flg_gcos = Option.is_some batch && (not iliad_pro) && not cfir; + flg_tri_ebcdic = (match batch with Some 1 -> true | _ -> false); + flg_short = short; + flg_register = register; + flg_optim_min_max = optim_min_max; + flg_extraction = extraction; + flg_genere_libelle_restituee = output_labels; + flg_controle_separe = separate_controls; + flg_controle_immediat = immediate_controls; + flg_overlays = overlays; + flg_colors = colored_output; + flg_ticket = ticket; + flg_trace = trace; + flg_debug = debug || trace; + nb_debug_c; + xflg = cross_references; + } let process_dgfip_options ~application_names options = let options = Array.of_list ("mlang" :: options) in diff --git a/src/mlang/utils/dgfip_options.mli b/src/mlang/utils/dgfip_options.mli new file mode 100644 index 000000000..697d2da75 --- /dev/null +++ b/src/mlang/utils/dgfip_options.mli @@ -0,0 +1,27 @@ +val handler : + application_names:string list -> + int -> + bool -> + bool -> + int option -> + bool -> + bool -> + bool -> + bool -> + bool -> + bool -> + bool -> + bool -> + bool -> + bool -> + int -> + bool -> + bool -> + bool -> + bool -> + Config.Dgfip_options.flags + +val process_dgfip_options : + application_names:string list -> + string list -> + Config.Dgfip_options.flags option From f3078314f27ca6f26e9ac75284830ff290472ac2 Mon Sep 17 00:00:00 2001 From: Alexandre Doussot Date: Tue, 2 Dec 2025 12:03:52 +0100 Subject: [PATCH 02/14] remove dep_graph_file option --- src/mlang/driver.ml | 25 ++++++++++++------------- src/mlang/utils/cli.ml | 19 +++++-------------- src/mlang/utils/cli.mli | 2 -- src/mlang/utils/config.ml | 18 +++++++----------- src/mlang/utils/config.mli | 5 ----- 5 files changed, 24 insertions(+), 45 deletions(-) diff --git a/src/mlang/driver.ml b/src/mlang/driver.ml index 97626de9b..c07f81d96 100644 --- a/src/mlang/driver.ml +++ b/src/mlang/driver.ml @@ -153,14 +153,13 @@ let parse () = let set_opts (files : string list) (application_names : string list) (without_dgfip_m : bool) (debug : bool) (var_info_debug : string list) - (display_time : bool) (dep_graph_file : string) (print_cycles : bool) - (backend : string option) (output : string option) - (run_tests : string option) (dgfip_test_filter : bool) - (run_test : string option) (mpp_function : string) - (optimize_unsafe_float : bool) (precision : string option) - (roundops : string option) (comparison_error_margin : float option) - (income_year : int) (m_clean_calls : bool) - (dgfip_options : string list option) = + (display_time : bool) (print_cycles : bool) (backend : string option) + (output : string option) (run_tests : string option) + (dgfip_test_filter : bool) (run_test : string option) + (mpp_function : string) (optimize_unsafe_float : bool) + (precision : string option) (roundops : string option) + (comparison_error_margin : float option) (income_year : int) + (m_clean_calls : bool) (dgfip_options : string list option) = let value_sort = let precision = Option.get precision in if precision = "double" then Config.RegularFloat @@ -223,14 +222,14 @@ let set_opts (files : string list) (application_names : string list) process_dgfip_options !Config.backend ~application_names dgfip_options in Config.set_all_arg_refs files application_names without_dgfip_m debug - var_info_debug display_time dep_graph_file print_cycles output - optimize_unsafe_float m_clean_calls comparison_error_margin income_year - value_sort round_ops backend dgfip_test_filter mpp_function dgfip_flags - execution_mode + var_info_debug display_time print_cycles output optimize_unsafe_float + m_clean_calls comparison_error_margin income_year value_sort round_ops + backend dgfip_test_filter mpp_function dgfip_flags execution_mode let run_single_test m_program test = Mir_interpreter.repl_debug := true; - Test_interpreter.check_one_test m_program test !Config.value_sort !Config.round_ops; + Test_interpreter.check_one_test m_program test !Config.value_sort + !Config.round_ops; Cli.result_print "Test passed!" let run_multiple_tests m_program tests = diff --git a/src/mlang/utils/cli.ml b/src/mlang/utils/cli.ml index 2970f891b..f97601372 100644 --- a/src/mlang/utils/cli.ml +++ b/src/mlang/utils/cli.ml @@ -58,15 +58,6 @@ let display_time = & info [ "display_time"; "t" ] ~doc:"Displays timing information (use with --debug)") -let dep_graph_file = - let doc = - "Name of the file where the variable dependency graph should be output \ - (use with --debug)" - in - Arg.( - value & opt file "dep_graph.dot" - & info [ "dep_graph_file"; "g" ] ~docv:"DEP_GRAPH" ~doc) - let no_print_cycles = let doc = "If set, disable the eventual circular dependencies repport" in Arg.(value & flag & info [ "no_print_cycles"; "c" ] ~doc) @@ -164,7 +155,7 @@ let comparison_error_margin_cli = let income_year_cli = Arg.( value - & opt (int) (1900 + (Unix.localtime (Unix.time())).Unix.tm_year - 1) + & opt int (1900 + (Unix.localtime (Unix.time ())).Unix.tm_year - 1) & info [ "income-year" ] ~docv:"INCOME_YEAR" ~doc:"Set the year of the income.") @@ -189,10 +180,10 @@ let dgfip_options = let mlang_t f = Term.( const f $ files $ applications $ without_dgfip_m $ debug $ var_info_debug - $ display_time $ dep_graph_file $ no_print_cycles $ backend $ output - $ run_all_tests $ dgfip_test_filter $ run_test $ mpp_function - $ optimize_unsafe_float $ precision $ roundops $ comparison_error_margin_cli - $ income_year_cli $ m_clean_calls $ dgfip_options) + $ display_time $ no_print_cycles $ backend $ output $ run_all_tests + $ dgfip_test_filter $ run_test $ mpp_function $ optimize_unsafe_float + $ precision $ roundops $ comparison_error_margin_cli $ income_year_cli + $ m_clean_calls $ dgfip_options) let info = let doc = diff --git a/src/mlang/utils/cli.mli b/src/mlang/utils/cli.mli index 259cdb772..bfeb9c31d 100644 --- a/src/mlang/utils/cli.mli +++ b/src/mlang/utils/cli.mli @@ -25,7 +25,6 @@ val mlang_t : bool -> string list -> bool -> - string -> bool -> string option -> string option -> @@ -47,7 +46,6 @@ val mlang_t : val info : Cmdliner.Cmd.info (** Command-line man page for --help *) - val add_prefix_to_each_line : string -> (int -> string) -> string (** [add_prefix_to_each_line msg prefix] will print msg but each line with line number [i] starts with the string [prefix i]*) diff --git a/src/mlang/utils/config.ml b/src/mlang/utils/config.ml index 036d98c68..b608140e7 100644 --- a/src/mlang/utils/config.ml +++ b/src/mlang/utils/config.ml @@ -95,8 +95,6 @@ let application_names : string list ref = ref [] let without_dgfip_m = ref false -let dbg_graph_file : string ref = ref "dbg_graph.dot" - let verify_flag = ref false let debug_flag = ref false @@ -139,13 +137,12 @@ let income_year = ref 0 let set_all_arg_refs (files_ : files) applications_ (without_dgfip_m_ : bool) (debug_ : bool) (var_info_debug_ : string list) (display_time_ : bool) - (dbg_graph_file_ : string) (no_print_cycles_ : bool) - (output_file_ : string option) (optimize_unsafe_float_ : bool) - (m_clean_calls_ : bool) (comparison_error_margin_ : float option) - (income_year_ : int) (value_sort_ : value_sort) - (round_ops_ : round_ops) (backend_ : backend) (dgfip_test_filter_ : bool) - (mpp_function_ : string) (dgfip_flags_ : Dgfip_options.flags) - (execution_mode_ : execution_mode) = + (no_print_cycles_ : bool) (output_file_ : string option) + (optimize_unsafe_float_ : bool) (m_clean_calls_ : bool) + (comparison_error_margin_ : float option) (income_year_ : int) + (value_sort_ : value_sort) (round_ops_ : round_ops) (backend_ : backend) + (dgfip_test_filter_ : bool) (mpp_function_ : string) + (dgfip_flags_ : Dgfip_options.flags) (execution_mode_ : execution_mode) = source_files := files_; application_names := applications_; without_dgfip_m := without_dgfip_m_; @@ -153,12 +150,11 @@ let set_all_arg_refs (files_ : files) applications_ (without_dgfip_m_ : bool) var_info_debug := var_info_debug_; var_info_flag := !var_info_debug <> []; display_time := display_time_; - dbg_graph_file := dbg_graph_file_; no_print_cycles_flag := no_print_cycles_; optimize_unsafe_float := optimize_unsafe_float_; m_clean_calls := m_clean_calls_; execution_mode := execution_mode_; - income_year := income_year_; + income_year := income_year_; value_sort := value_sort_; round_ops := round_ops_; backend := backend_; diff --git a/src/mlang/utils/config.mli b/src/mlang/utils/config.mli index fd6b29209..0599477bf 100644 --- a/src/mlang/utils/config.mli +++ b/src/mlang/utils/config.mli @@ -67,9 +67,6 @@ val source_files : files ref val application_names : string list ref -val dbg_graph_file : string ref -(** Prefix for debug graph output files *) - val without_dgfip_m : bool ref val verify_flag : bool ref @@ -128,7 +125,6 @@ val set_all_arg_refs : (* debug *) bool -> (* var_info_debug *) string list -> (* display_time *) bool -> - (* dbg_graph_file *) string -> (* prints_cycles *) bool -> (* output_file *) string option -> (* optimize_unsafe_float *) bool -> @@ -143,4 +139,3 @@ val set_all_arg_refs : (* dgfip_flags *) Dgfip_options.flags -> (* execution_mode *) execution_mode -> unit - From be26e2e4038ba5df1acbfe93f2c6e6425bc7df2a Mon Sep 17 00:00:00 2001 From: Alexandre Doussot Date: Tue, 2 Dec 2025 11:11:38 +0100 Subject: [PATCH 03/14] my makefile update --- Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Makefile b/Makefile index 755ed41af..965170ffd 100644 --- a/Makefile +++ b/Makefile @@ -39,3 +39,11 @@ clean: FORCE remise_a_zero_versionnage rm -f doc/doc.html dune clean +test: + _build/default/src/main.exe tests/mlang/${test}.m -A test --mpp_function test_args --dgfip_options='' --run_test tests/mlang/${test}.irj --debug + +c: + _build/default/src/main.exe tests/mlang/${test}.m -A app -b dgfip_c --mpp_function target --dgfip_options='' --output output/${test}.c --debug + +t: + dune exec src/main.exe --profile release -- -A iliad --display_time --precision double --income-year=2020 --comparison_error_margin=0.000001 --mpp_function=enchainement_primitif_interpreteur ir-calcul/sources2020m_6_5/tgvI.m ir-calcul/sources2020m_6_5/errI.m ir-calcul/sources2020m_6_5/chap-1.m ir-calcul/sources2020m_6_5/chap-2.m ir-calcul/sources2020m_6_5/chap-3.m ir-calcul/sources2020m_6_5/chap-4.m ir-calcul/sources2020m_6_5/chap-51.m ir-calcul/sources2020m_6_5/chap-52.m ir-calcul/sources2020m_6_5/chap-6.m ir-calcul/sources2020m_6_5/chap-7.m ir-calcul/sources2020m_6_5/chap-81.m ir-calcul/sources2020m_6_5/chap-82.m ir-calcul/sources2020m_6_5/chap-83.m ir-calcul/sources2020m_6_5/chap-84.m ir-calcul/sources2020m_6_5/chap-85.m ir-calcul/sources2020m_6_5/chap-86.m ir-calcul/sources2020m_6_5/chap-87.m ir-calcul/sources2020m_6_5/chap-88.m ir-calcul/sources2020m_6_5/chap-aff.m ir-calcul/sources2020m_6_5/chap-cinr.m ir-calcul/sources2020m_6_5/chap-cmajo.m ir-calcul/sources2020m_6_5/chap-cor.m ir-calcul/sources2020m_6_5/chap-ctl.m ir-calcul/sources2020m_6_5/chap-ini.m ir-calcul/sources2020m_6_5/chap-inr.m ir-calcul/sources2020m_6_5/chap-isf.m ir-calcul/sources2020m_6_5/chap-majo.m ir-calcul/sources2020m_6_5/chap-perp.m ir-calcul/sources2020m_6_5/chap-plaf.m ir-calcul/sources2020m_6_5/chap-taux.m ir-calcul/sources2020m_6_5/chap-teff.m ir-calcul/sources2020m_6_5/chap-thr.m ir-calcul/sources2020m_6_5/chap-tl.m ir-calcul/sources2020m_6_5/coc1.m ir-calcul/sources2020m_6_5/coc2.m ir-calcul/sources2020m_6_5/coc3.m ir-calcul/sources2020m_6_5/coc4.m ir-calcul/sources2020m_6_5/coc5.m ir-calcul/sources2020m_6_5/coc7.m ir-calcul/sources2020m_6_5/coi1.m ir-calcul/sources2020m_6_5/coi2.m ir-calcul/sources2020m_6_5/coi3.m ir-calcul/sources2020m_6_5/horizoc.m ir-calcul/sources2020m_6_5/horizoi.m ir-calcul/sources2020m_6_5/res-ser1.m ir-calcul/sources2020m_6_5/res-ser2.m m_ext/2020/cibles.m --run_test='tests/2020/fuzzing/fuzzer_1423.m_test' --dgfip_options='' --debug From c055c8b7bf20704bc3d2fbb616a9670b58ed6324 Mon Sep 17 00:00:00 2001 From: Alexandre Doussot Date: Tue, 2 Dec 2025 12:11:09 +0100 Subject: [PATCH 04/14] add dbg_info structure --- src/mlang/dune | 2 +- src/mlang/m_ir/dbg_info.ml | 217 +++++++++++++++++++++++++++++++++++++ 2 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 src/mlang/m_ir/dbg_info.ml diff --git a/src/mlang/dune b/src/mlang/dune index 44f12dbe2..e8add767c 100644 --- a/src/mlang/dune +++ b/src/mlang/dune @@ -3,7 +3,7 @@ (library (public_name mlang) (libraries re ANSITerminal parmap cmdliner threads dune-build-info num gmp - menhirLib)) + menhirLib ocamlgraph yojson)) (documentation (package mlang) diff --git a/src/mlang/m_ir/dbg_info.ml b/src/mlang/m_ir/dbg_info.ml new file mode 100644 index 000000000..1d830eb40 --- /dev/null +++ b/src/mlang/m_ir/dbg_info.ml @@ -0,0 +1,217 @@ +module Origin = struct + type code = Rule of int | Declared | Input | Target of string | Const + + type t = { filename : string; sline : int; eline : int; code_orig : code } + + let make filename sline eline code_orig = + { filename; sline; eline; code_orig } + + let make_from_pos pos code_orig = + let filename = Pos.get_file pos in + let sline = Pos.get_start_line pos in + let eline = Pos.get_end_line pos in + { filename; sline; eline; code_orig } + + let hash (t : t) = Hashtbl.hash t + + let to_json origin = + let code_orig = + match origin.code_orig with + | Rule i -> Format.asprintf "%d" i + | Input -> "input" + | Declared -> "Declared" + | Target s -> Format.asprintf "target-%s" s + | Const -> "const" + in + Format.asprintf + {|"origin": {"code_orig": "%s", "file": "%s", "sline": %d, "eline": %d }|} + code_orig origin.filename origin.sline origin.eline +end + +module Tick = struct + include Int + + let inner = ref (-1) + + let tick () = + incr inner; + !inner + + module Map = struct + include IntMap + end +end + +module Info = struct + type t = { + name : string; + var : Com.Var.t; + value : Com.literal; + origin : Origin.t; + } + (* We've removed idx_opt, it may be needed for tables. *) + + let make name var value origin = { name; var; value; origin } + + module Runtime = struct + type t = { hash : int; value : Com.literal; name : string option } + + let make origin value name = { hash = Origin.hash origin; value; name } + end + + module Static = struct + type t = { + name : string; + origin : Origin.t; + is_input : bool; + descr : string option; + } + + let make name origin is_input descr = { name; origin; is_input; descr } + end +end + +module Const = struct + type t = { name : string; value : Com.literal; origin : Origin.t } + + let make name value fname sline eline = + let origin = Origin.make fname sline eline Const in + { name; value; origin } + + let make_from_pos name value pos = + let origin = Origin.make_from_pos pos Const in + { name; value; origin } +end + +module Vertex = struct + type kind = Literal | Var + + include Tick + + (* Invariant (to be verified): All ticks are different *) + type t = Tick.t + + (* This feels weird, but String.hash was introduced in 5.0 *) + let hash t = Hashtbl.hash t +end + +module Graph = Graph.Persistent.Digraph.Concrete (Vertex) + +module TickMap = struct + include StrMap + + let find name map = + match StrMap.find_opt name map with + | None -> + let msg = + if StrMap.card map > 100 then + Format.asprintf "could not find %s in tick_map (too long).@." name + else + Format.asprintf "could not find %s in tick_map %a.@." name + (StrMap.pp (fun fmt -> Format.fprintf fmt "%d")) + map + in + raise @@ Failure msg + | Some tick -> tick +end + +type t = { + graph : Graph.t; + runtimes : Info.Runtime.t Tick.Map.t; + statics : Info.Static.t IntMap.t; + consts : Const.t IntMap.t; + literals : string IntMap.t; + ledger : Tick.t StrMap.t; +} + +let empty = + { + graph = Graph.empty; + runtimes = Tick.Map.empty; + statics = IntMap.empty; + consts = IntMap.empty; + literals = IntMap.empty; + ledger = StrMap.empty; + } + +let to_json (fmt : Format.formatter) info : unit = + let open Format in + let open Info.Static in + let open Info.Runtime in + let open Const in + let delim = ref "" in + Format.fprintf fmt {|{"graph":{@. "nodes": [|}; + let pp_vertex v = + let var = Graph.V.label v in + Format.fprintf fmt {|%s@.{"data": "%d"}|} !delim var; + (* Small hack to avoid trailing commas *) + delim := "," + in + Format.printf "writing vertices...@."; + Graph.iter_vertex pp_vertex info.graph; + fprintf fmt {|],@. "edges": [|}; + let print_edge (e : Graph.E.t) = + let src = Graph.E.src e in + let dst = Graph.E.dst e in + let src = Graph.V.label src in + let dst = Graph.V.label dst in + Format.fprintf fmt {|%s@.{"data": {"source": "%d", "target": "%d"}}|} !delim + src dst; + delim := "," + in + delim := ""; + Format.printf "writing edges...@."; + Graph.iter_edges_e print_edge info.graph; + let print_static_info hash { name; origin; is_input; descr } = + let origin = Origin.to_json origin in + let descr = + match descr with + | None -> "" + | Some descr -> + let descr = Yojson.Safe.to_string (`String descr) in + asprintf {|"descr": %s,|} descr + in + Format.fprintf fmt {|%s@."%d": {"name": %S, "is_input": %b, %s %s}|} !delim + hash name is_input descr origin; + delim := "," + in + Format.fprintf fmt "]},@."; + Format.printf "writing info...@."; + delim := ""; + Format.fprintf fmt {|"statics": {@.|}; + IntMap.iter print_static_info info.statics; + Format.fprintf fmt "},@."; + delim := ""; + Format.fprintf fmt {|"runtimes": {@.|}; + let print_runtime_info tick { value; hash; name } = + let name = + match name with + | None -> "" + | Some name -> asprintf {|, "name" : %S|} name + in + Format.fprintf fmt {|%s@."%d": {"value": "%a", "hash": %d %s}|} !delim tick + Com.format_literal value hash name; + delim := "," + in + Tick.Map.iter print_runtime_info info.runtimes; + let print_const id const = + Format.printf "Printing consts!!!!@."; + let origin = Origin.to_json const.origin in + Format.fprintf fmt + {|%s@."%d": {"name": %S, "value": "%a", "kind": "const", %s}|} !delim id + const.name Com.format_literal const.value origin; + delim := "," + in + IntMap.iter print_const info.consts; + let print_lit id lit = + Format.fprintf fmt {|%s@."%d": {"name": %S}|} !delim id lit; + delim := "," + in + IntMap.iter print_lit info.literals; + Format.fprintf fmt "}}@." + +let write_json_file filename info = + let filename = filename ^ ".json" in + let oc = open_out filename in + let fmt = Format.formatter_of_out_channel oc in + Format.fprintf fmt "%a@." to_json info From b9f7fceea6236556acf432f8ee72ad19b6b80fcc Mon Sep 17 00:00:00 2001 From: Alexandre Doussot Date: Tue, 2 Dec 2025 15:23:58 +0100 Subject: [PATCH 05/14] add tracing to the interpreter --- src/mlang/m_ir/com.ml | 52 ++++++ src/mlang/m_ir/com.mli | 10 + src/mlang/m_ir/mir_interpreter.ml | 182 +++++++++++++++++-- src/mlang/m_ir/mir_interpreter.mli | 46 ++--- src/mlang/test_framework/test_interpreter.ml | 5 +- 5 files changed, 249 insertions(+), 46 deletions(-) diff --git a/src/mlang/m_ir/com.ml b/src/mlang/m_ir/com.ml index 5af0bfd95..b7062ee26 100644 --- a/src/mlang/m_ir/com.ml +++ b/src/mlang/m_ir/com.ml @@ -520,6 +520,58 @@ and 'v expression = and 'v m_expression = 'v expression Pos.marked +type const = { id : string; value : literal; pos : Pos.t } + +type 'v dep = + | Tab of 'v * 'v m_expression + | V of 'v + | LiteralDep of literal + | Const of const + +(* This code was taken from Noe and adapted to the 2025 var architecture *) +let get_used_variables (e : 'v expression) : + ('v dep * 'v expression option) list = + let rec get_used_variables_ (e : 'v expression) + (acc : ('v dep * 'v expression option) list) = + match e with + | TestInSet (_, Mark (e, _), _) | Unop (_, Mark (e, _)) -> + let acc = get_used_variables_ e acc in + acc + | Comparison (_, Mark (e1, _), Mark (e2, _)) + | Binop (_, Mark (e1, _), Mark (e2, _)) -> + let acc = get_used_variables_ e1 acc in + let acc = get_used_variables_ e2 acc in + acc + | Conditional (Mark (e1, _), Mark (e2, _), e3) -> ( + let acc = get_used_variables_ e1 acc in + let acc = get_used_variables_ e2 acc in + match e3 with + | None -> acc + | Some (Mark (e3, _)) -> get_used_variables_ e3 acc) + | FuncCall (_, args) -> + List.fold_left + (fun acc arg -> get_used_variables_ (Pos.unmark arg) acc) + acc args + | FuncCallLoop _ | Loop _ -> assert false + | Var var + | Size (Mark (var, _)) + | Attribut (Mark (var, _), _) + | IsVariable (Mark (var, _), _) -> ( + match var with + | TabAccess (_, v, m_i) -> (Tab (v, m_i), None) :: acc + | VarAccess (_, v) -> (V v, None) :: acc + | FieldAccess (_, Mark (v, _), _, _) -> get_used_variables_ v acc) + | Literal lit -> (LiteralDep lit, None) :: acc + (* | Literal { lit; origin = Some (Mark (id, pos)) } -> *) + (* (Const { id; value = lit; pos }, None) :: acc *) + (* | Literal { lit; origin = None } -> *) + (* (LiteralDep lit, None) :: acc *) + | NbCategory _ | NbAnomalies | NbDiscordances | NbInformatives + | NbBloquantes -> + acc + in + get_used_variables_ e [] + module Error = struct type typ = Anomaly | Discordance | Information diff --git a/src/mlang/m_ir/com.mli b/src/mlang/m_ir/com.mli index 4484cb76b..1936dbe2b 100644 --- a/src/mlang/m_ir/com.mli +++ b/src/mlang/m_ir/com.mli @@ -316,6 +316,16 @@ and 'v expression = and 'v m_expression = 'v expression Pos.marked +type const = { id : string; value : literal; pos : Pos.t } + +type 'v dep = + | Tab of 'v * 'v m_expression + | V of 'v + | LiteralDep of literal + | Const of const + +val get_used_variables : 'v expression -> ('v dep * 'v expression option) list + module Error : sig type typ = Anomaly | Discordance | Information diff --git a/src/mlang/m_ir/mir_interpreter.ml b/src/mlang/m_ir/mir_interpreter.ml index 9b5d54db2..db2a45fac 100644 --- a/src/mlang/m_ir/mir_interpreter.ml +++ b/src/mlang/m_ir/mir_interpreter.ml @@ -44,6 +44,8 @@ module type S = sig base : value Array.t; } + type ctx_exec_ctx = CtxUndefined | CtxTarget of string | CtxRule of int + type ctx = { ctx_prog : Mir.program; mutable ctx_target : Mir.target; @@ -64,10 +66,13 @@ module type S = sig mutable ctx_nb_bloquantes : int; mutable ctx_finalized_anos : (Com.Error.t * string option) list; mutable ctx_exported_anos : (Com.Error.t * string option) list; - mutable ctx_events : (value, Com.Var.t) Com.event_value Array.t Array.t list; + mutable ctx_events : + (value, Com.Var.t) Com.event_value Array.t Array.t list; + mutable ctx_dbg_info : Dbg_info.t option; + mutable ctx_exec_ctx : ctx_exec_ctx; } - val empty_ctx : Mir.program -> ctx + val empty_ctx : Mir.program -> Dbg_info.t option -> ctx val literal_to_value : Com.literal -> value @@ -141,6 +146,8 @@ struct base : value Array.t; } + type ctx_exec_ctx = CtxUndefined | CtxTarget of string | CtxRule of int + type ctx = { ctx_prog : Mir.program; mutable ctx_target : Mir.target; @@ -161,10 +168,13 @@ struct mutable ctx_nb_bloquantes : int; mutable ctx_finalized_anos : (Com.Error.t * string option) list; mutable ctx_exported_anos : (Com.Error.t * string option) list; - mutable ctx_events : (value, Com.Var.t) Com.event_value Array.t Array.t list; + mutable ctx_events : + (value, Com.Var.t) Com.event_value Array.t Array.t list; + mutable ctx_dbg_info : Dbg_info.t option; + mutable ctx_exec_ctx : ctx_exec_ctx; } - let empty_ctx (p : Mir.program) : ctx = + let empty_ctx (p : Mir.program) (dbg_info : Dbg_info.t option) : ctx = let dummy_var = Com.Var.new_ref ~name:(Pos.without "") in let init_tmp_var _i = { var = dummy_var; value = Undefined } in let init_ref _i = @@ -201,6 +211,7 @@ struct in Array.init (IntMap.cardinal p.program_var_spaces_idx) init in + let ctx_dbg_info = dbg_info in { ctx_prog = p; ctx_target = snd (StrMap.min_binding p.program_targets); @@ -222,6 +233,8 @@ struct ctx_finalized_anos = []; ctx_exported_anos = []; ctx_events = []; + ctx_dbg_info; + ctx_exec_ctx = CtxUndefined; } let literal_to_value (l : Com.literal) : value = @@ -502,10 +515,138 @@ struct done else set_var_value_org ctx vsd v vorg value + and eval_m_index ctx m_i = + match evaluate_expr ctx m_i with + | Number z -> Int64.to_string @@ N.to_int z + | Undefined -> "indefini" + + and trace_deps deps dbg_info ctx = + let open Dbg_info in + let trace_dep (ticks, dbg_info) dep = + match fst dep with + | Com.V var -> + let name = Com.Var.name_str var in + (* For now, we add uninstantiated depedencies as undefined *) + begin + match TickMap.find name dbg_info.ledger with + | exception Failure msg -> + Format.fprintf Format.err_formatter "%s" msg; + let tick = Tick.tick () in + Format.fprintf Format.err_formatter "it will have tick: %d@." + tick; + let pos = Com.Var.name var |> Pos.get in + let origin = Origin.make_from_pos pos Declared in + let ledger = StrMap.add name tick dbg_info.ledger in + let runtime = Info.Runtime.make origin Undefined (Some name) in + let runtimes = Tick.Map.add tick runtime dbg_info.runtimes in + let static = Info.Static.make name origin false None in + let statics = IntMap.add runtime.hash static dbg_info.statics in + let dbg_info = { dbg_info with ledger; runtimes; statics } in + (tick :: ticks, dbg_info) + | tick -> (tick :: ticks, dbg_info) + end + | Const const -> + let name = const.Com.id in + begin + match TickMap.find name dbg_info.ledger with + | tick -> (tick :: ticks, dbg_info) + | exception Failure _ -> + let tick = Tick.tick () in + let const = + Const.make_from_pos name const.Com.value const.pos + in + let consts = Tick.Map.add tick const dbg_info.consts in + let ledger = StrMap.add name tick dbg_info.ledger in + let dbg_info = { dbg_info with consts; ledger } in + (tick :: ticks, dbg_info) + end + | Tab (var, m_i) -> + let name = Com.Var.name_str var in + let idx_str = eval_m_index ctx m_i in + let name = Format.asprintf "%s[%s]" name idx_str in + begin + match TickMap.find name dbg_info.ledger with + | exception Failure _ -> + let tick = Tick.tick () in + let pos = Com.Var.name var |> Pos.get in + let origin = Origin.make_from_pos pos Declared in + let ledger = StrMap.add name tick dbg_info.ledger in + let runtime = Info.Runtime.make origin Undefined (Some name) in + let runtimes = Tick.Map.add tick runtime dbg_info.runtimes in + let static = Info.Static.make name origin false None in + let statics = IntMap.add runtime.hash static dbg_info.statics in + let dbg_info = { dbg_info with ledger; runtimes; statics } in + (tick :: ticks, dbg_info) + | tick -> (tick :: ticks, dbg_info) + end + | LiteralDep _lit -> (ticks, dbg_info) + in + + List.fold_left trace_dep ([], dbg_info) deps + and set_access ctx access vexpr = match get_access_var ctx access with - | Some (vsd, v) -> set_var_value ctx vsd v @@ evaluate_expr ctx vexpr | None -> () + | Some (vsd, v) -> ( + let value = evaluate_expr ctx vexpr in + set_var_value ctx vsd v value; + match (ctx.ctx_dbg_info, ctx.ctx_exec_ctx) with + | None, _ -> () + (* | _, CtxTarget "effacer_base_etc" *) + (* | _, CtxTarget "effacer_avfisc_1" *) + (* | _, CtxTarget "effacer_calculee_etc" -> *) + (* () *) + | Some dbg_info, _ -> + let open Dbg_info in + let deps = Com.get_used_variables @@ Pos.unmark vexpr in + let ticks, dbg_info = trace_deps deps dbg_info ctx in + (* Create the tick for this variable after the deps so that they are + in the right order on marple side. *) + let tick = Tick.tick () in + let access_name name = + match access with + | Com.VarAccess _ -> name + | Com.TabAccess (_, v, m_i) -> + let name = Com.Var.name_str v in + let idx_str = eval_m_index ctx m_i in + Format.asprintf "%s[%s]" name idx_str + | Com.FieldAccess (_, _, _, _) -> Com.Var.name_str v + in + let name = access_name @@ Com.Var.name_str v in + let is_input = + match Com.Var.cat_var_loc v with + | Com.CatVar.LocInput -> true + | (exception Failure _) | _ -> false + in + let pos = Pos.get vexpr in + let rule_id = + match ctx.ctx_exec_ctx with + | CtxRule i -> Dbg_info.Origin.Rule i + | CtxTarget s -> Dbg_info.Origin.Target s + (* FIXME: This is a debug failure, do not release as-if *) + | CtxUndefined -> raise @@ Failure "no rule id" + in + let lit_value = value_to_literal value in + let descr = + match Com.Var.descr_str v with + | exception _ -> None + | descr -> Some descr + in + let origin = Origin.make_from_pos pos rule_id in + let runtime = Info.Runtime.make origin lit_value (Some name) in + let runtimes = Tick.Map.add tick runtime dbg_info.runtimes in + let static = Info.Static.make name origin is_input descr in + let statics = IntMap.add runtime.hash static dbg_info.statics in + let vert = Dbg_info.Graph.V.create tick in + let graph = dbg_info.graph in + let add_edge graph deptick = + let dep_vert = Dbg_info.Graph.V.create deptick in + Dbg_info.Graph.add_edge graph vert dep_vert + in + let graph = List.fold_left add_edge graph ticks in + let ledger = TickMap.add name tick dbg_info.ledger in + ctx.ctx_dbg_info <- + Some { dbg_info with graph; runtimes; statics; ledger }) and evaluate_expr (ctx : ctx) (e : Mir.expression Pos.marked) : value = let comparison op new_e1 new_e2 = @@ -1150,6 +1291,22 @@ struct and evaluate_target (canBlock : bool) (ctx : ctx) (target : Mir.target) (args : Mir.m_access list) : unit = + (* We check if the current target is in the rule map. + If it is, we assume we're in a rule, and register it + to annotate the value we'll set later in the dbg_info. *) + let target_name = Pos.unmark target.target_name in + Format.printf "evaluating target: %s@." target_name; + let rule_id = + IntMap.fold + (fun i str acc -> + match acc with + | Some _ -> acc + | None -> if str = target_name then Some i else None) + ctx.ctx_prog.program_rules None + in + (match rule_id with + | None -> ctx.ctx_exec_ctx <- CtxTarget target_name + | Some rule_id -> ctx.ctx_exec_ctx <- CtxRule rule_id); let rec set_args n vl al = match (vl, al) with | v :: vl', m_a :: al' -> ( @@ -1300,11 +1457,12 @@ let prepare_interp (sort : Config.value_sort) (roundops : Config.round_ops) : let evaluate_program (p : Mir.program) (inputs : Com.literal Com.Var.Map.t) (events : (Com.literal, Com.Var.t) Com.event_value StrMap.t list) - (sort : Config.value_sort) (roundops : Config.round_ops) : - Com.literal Com.Var.Map.t * Com.Error.Set.t = + (sort : Config.value_sort) (roundops : Config.round_ops) + (dbg_info : Dbg_info.t option) : + Com.literal Com.Var.Map.t * Com.Error.Set.t * Dbg_info.t option = prepare_interp sort roundops; let module Interp = (val get_interp sort roundops : S) in - let ctx = Interp.empty_ctx p in + let ctx = Interp.empty_ctx p dbg_info in Interp.update_ctx_with_inputs ctx inputs; Interp.update_ctx_with_events ctx events; Interp.evaluate_program ctx; @@ -1332,9 +1490,11 @@ let evaluate_program (p : Mir.program) (inputs : Com.literal Com.Var.Map.t) let fold res (e, _) = Com.Error.Set.add e res in List.fold_left fold Com.Error.Set.empty ctx.ctx_exported_anos in - (varMap, anoSet) + let dbg_info = ctx.ctx_dbg_info in + (varMap, anoSet, dbg_info) let evaluate_expr (p : Mir.program) (e : Mir.expression Pos.marked) - (sort : Config.value_sort) (roundops : Config.round_ops) : Com.literal = + (sort : Config.value_sort) (roundops : Config.round_ops) + (dbg_info : Dbg_info.t option) : Com.literal = let module Interp = (val get_interp sort roundops : S) in - Interp.value_to_literal (Interp.evaluate_expr (Interp.empty_ctx p) e) + Interp.value_to_literal (Interp.evaluate_expr (Interp.empty_ctx p dbg_info) e) diff --git a/src/mlang/m_ir/mir_interpreter.mli b/src/mlang/m_ir/mir_interpreter.mli index 36c7e3c79..d13f8b396 100644 --- a/src/mlang/m_ir/mir_interpreter.mli +++ b/src/mlang/m_ir/mir_interpreter.mli @@ -65,6 +65,11 @@ module type S = sig base : value Array.t; } + type ctx_exec_ctx = + | CtxUndefined + | CtxTarget of string + | CtxRule of int (** Marker to in which context are variables set *) + type ctx = { ctx_prog : Mir.program; mutable ctx_target : Mir.target; @@ -85,11 +90,14 @@ module type S = sig mutable ctx_nb_bloquantes : int; mutable ctx_finalized_anos : (Com.Error.t * string option) list; mutable ctx_exported_anos : (Com.Error.t * string option) list; - mutable ctx_events : (value, Com.Var.t) Com.event_value Array.t Array.t list; + mutable ctx_events : + (value, Com.Var.t) Com.event_value Array.t Array.t list; + mutable ctx_dbg_info : Dbg_info.t option; + mutable ctx_exec_ctx : ctx_exec_ctx; } (** Interpretation context *) - val empty_ctx : Mir.program -> ctx + val empty_ctx : Mir.program -> Dbg_info.t option -> ctx val literal_to_value : Com.literal -> value @@ -140,36 +148,6 @@ module FloatDefInterp : - Multi: use the rouding operations of the PC/multi-thread context - Mf: use the rounding operations of the mainframe context *) -module FloatMultInterp : - S with type custom_float = Mir_number.RegularFloatNumber.t - -module FloatMfInterp : - S with type custom_float = Mir_number.RegularFloatNumber.t - -module MPFRDefInterp : S with type custom_float = Mir_number.MPFRNumber.t - -module MPFRMultInterp : S with type custom_float = Mir_number.MPFRNumber.t - -module MPFRMfInterp : S with type custom_float = Mir_number.MPFRNumber.t - -module BigIntDefInterp : S - -module BigIntMultInterp : S - -module BigIntMfInterp : S - -module IntvDefInterp : S with type custom_float = Mir_number.IntervalNumber.t - -module IntvMultInterp : S with type custom_float = Mir_number.IntervalNumber.t - -module IntvMfInterp : S with type custom_float = Mir_number.IntervalNumber.t - -module RatDefInterp : S with type custom_float = Mir_number.RationalNumber.t - -module RatMultInterp : S with type custom_float = Mir_number.RationalNumber.t - -module RatMfInterp : S with type custom_float = Mir_number.RationalNumber.t - (** {1 Generic interpretation API}*) val get_interp : Config.value_sort -> Config.round_ops -> (module S) @@ -180,7 +158,8 @@ val evaluate_program : (Com.literal, Com.Var.t) Com.event_value StrMap.t list -> Config.value_sort -> Config.round_ops -> - Com.literal Com.Var.Map.t * Com.Error.Set.t + Dbg_info.t option -> + Com.literal Com.Var.Map.t * Com.Error.Set.t * Dbg_info.t option (** Main interpreter function *) val evaluate_expr : @@ -188,5 +167,6 @@ val evaluate_expr : Mir.expression Pos.marked -> Config.value_sort -> Config.round_ops -> + Dbg_info.t option -> Com.literal (** Interprets only an expression *) diff --git a/src/mlang/test_framework/test_interpreter.ml b/src/mlang/test_framework/test_interpreter.ml index 632ca918c..b2d8de5c8 100644 --- a/src/mlang/test_framework/test_interpreter.ml +++ b/src/mlang/test_framework/test_interpreter.ml @@ -185,9 +185,10 @@ let check_test (program : Mir.program) (test_name : string) Cli.debug_print "Executing program %s" inst.label; (* Cli.debug_print "Combined Program (w/o verif conds):@.%a@." Format_bir.format_program program; *) - let varMap, anoSet = + let dbg_info = None in + let varMap, anoSet, _dbg_info = Mir_interpreter.evaluate_program program inst.vars inst.events - value_sort round_ops + value_sort round_ops dbg_info in let nbErrs = check_vars inst.expectedVars varMap From e66f41c811e2e6c54808b067e2a138eda78ec6ad Mon Sep 17 00:00:00 2001 From: Alexandre Doussot Date: Mon, 8 Sep 2025 14:32:43 +0200 Subject: [PATCH 06/14] Add origin to literals --- src/mlang/backend_compilers/bir_to_dgfip_c.ml | 4 +-- src/mlang/driver.ml | 2 +- src/mlang/m_frontend/expander.ml | 25 ++++++++------- src/mlang/m_frontend/mast_to_mir.ml | 18 +++++------ src/mlang/m_frontend/mparser.mly | 10 +++--- src/mlang/m_frontend/parse_utils.ml | 2 +- src/mlang/m_frontend/validator.ml | 4 +-- src/mlang/m_ir/com.ml | 32 +++++++++++++------ src/mlang/m_ir/com.mli | 22 +++++++++++-- src/mlang/m_ir/mir.ml | 8 ++--- src/mlang/m_ir/mir_interpreter.ml | 6 ++-- 11 files changed, 84 insertions(+), 49 deletions(-) diff --git a/src/mlang/backend_compilers/bir_to_dgfip_c.ml b/src/mlang/backend_compilers/bir_to_dgfip_c.ml index 2b5ccbda8..57db28395 100644 --- a/src/mlang/backend_compilers/bir_to_dgfip_c.ml +++ b/src/mlang/backend_compilers/bir_to_dgfip_c.ml @@ -398,9 +398,9 @@ and generate_c_expr (p : Mir.program) (e : Mir.expression Pos.marked) : let value_comp = D.dinstr res_val in D.build_transitive_composition { set_vars; def_test; value_comp } | FuncCall _ -> assert false (* should not happen *) - | Literal (Float f) -> + | Literal { lit = Float f; _ } -> { set_vars = []; def_test = D.dtrue; value_comp = D.lit f } - | Literal Undefined -> + | Literal { lit = Undefined; _ } -> { set_vars = []; def_test = D.dfalse; value_comp = D.lit 0. } | Var (VarAccess (m_sp_opt, var)) -> let def_test = D.m_var m_sp_opt var Def in diff --git a/src/mlang/driver.ml b/src/mlang/driver.ml index c07f81d96..e96698374 100644 --- a/src/mlang/driver.ml +++ b/src/mlang/driver.ml @@ -63,7 +63,7 @@ let patch_rule_1 (backend : Config.backend) let m_access = Pos.without (Com.VarAccess (None, Pos.without (Com.Normal name))) in - let litt = Com.Literal (Com.Float (if value then 1.0 else 0.0)) in + let litt = Com.mk_lit (Com.Float (if value then 1.0 else 0.0)) in let cmd = Com.SingleFormula (VarDecl (m_access, Pos.without litt)) in Pos.without cmd :: l else l diff --git a/src/mlang/m_frontend/expander.ml b/src/mlang/m_frontend/expander.ml index 15629742f..a5e78dd95 100644 --- a/src/mlang/m_frontend/expander.ml +++ b/src/mlang/m_frontend/expander.ml @@ -308,7 +308,7 @@ let add_const (Pos.Mark (name, name_pos)) (Pos.Mark (cval, cval_pos)) const_map Err.constant_already_defined old_pos name_pos | None -> ( match cval with - | Com.AtomLiteral (Com.Float f) -> + | Com.AtomLiteral { lit = Com.Float f; _ } -> ConstMap.add name (Pos.mark f name_pos) const_map | Com.AtomVar (Pos.Mark (Com.Normal const, _)) -> ( match ConstMap.find_opt const const_map with @@ -335,7 +335,9 @@ let rec expand_variable (const_map : const_context) (loop_map : loop_context) match Pos.unmark m_var with | Com.Normal name -> ( match ConstMap.find_opt name const_map with - | Some (Pos.Mark (f, _)) -> Pos.same (Com.AtomLiteral (Float f)) m_var + | Some (Pos.Mark (f, pos)) -> + let atom = Com.mk_atomlit_from_const (Float f) @@ Pos.mark name pos in + Pos.same atom m_var | None -> Pos.same (Com.AtomVar m_var) m_var) | Com.Generic gen_name -> if List.length gen_name.Com.parameters == 0 then @@ -422,16 +424,16 @@ let var_or_int_value (const_map : const_context) match ConstMap.find_opt name const_map with | Some (Pos.Mark (fvalue, _)) -> IntIndex (int_of_float fvalue) | None -> VarIndex (Pos.unmark m_v)) - | Com.AtomLiteral (Com.Float f) -> IntIndex (int_of_float f) - | Com.AtomLiteral Com.Undefined -> assert false + | Com.AtomLiteral { lit = Com.Float f; _ } -> IntIndex (int_of_float f) + | Com.AtomLiteral { lit = Com.Undefined; _ } -> assert false let var_or_int (m_atom : Com.m_var_name Com.atom Pos.marked) = match Pos.unmark m_atom with | Com.AtomVar (Pos.Mark (Normal v, _)) -> VarName v | Com.AtomVar (Pos.Mark (Generic _, _)) -> Err.generic_variable_not_allowed_in_left_part_of_loop (Pos.get m_atom) - | Com.AtomLiteral (Com.Float f) -> RangeInt (int_of_float f) - | Com.AtomLiteral Com.Undefined -> assert false + | Com.AtomLiteral { lit = Com.Float f; _ } -> RangeInt (int_of_float f) + | Com.AtomLiteral { lit = Com.Undefined; _ } -> assert false let loop_variables_size (lpvl : loop_param_value list) (pos : Pos.t) = let size_err p = Err.loop_variables_have_different_sizes p in @@ -554,7 +556,7 @@ let expand_loop_variables (lvs : Com.m_var_name Com.loop_variables Pos.marked) type 'v access_or_literal = | ExpAccess of 'v Com.m_access - | ExpLiteral of Com.literal + | ExpLiteral of Com.literal_with_orig let rec expand_access (const_map : const_context) (loop_map : loop_context) (Pos.Mark (a, a_pos) : Com.m_var_name Com.m_access) : @@ -619,7 +621,8 @@ and expand_expression (const_map : const_context) (loop_map : loop_context) match set_value with | VarValue (Pos.Mark (a, a_pos)) -> ( match expand_access const_map loop_map (Pos.mark a a_pos) with - | ExpLiteral (Float f) -> FloatValue (Pos.mark f a_pos) + | ExpLiteral { lit = Float f; _ } -> + FloatValue (Pos.mark f a_pos) | ExpAccess m_a -> VarValue m_a | _ -> assert false) | FloatValue _ | IntervalValue _ -> set_value) @@ -674,7 +677,7 @@ and expand_expression (const_map : const_context) (loop_map : loop_context) List.fold_left (fun res loop_expr -> Pos.same (Binop (Pos.same Or m_expr, res, loop_expr)) m_expr) - (Pos.same (Literal (Float 0.0)) m_expr) + (Pos.same (Com.mk_lit (Float 0.0)) m_expr) loop_exprs | Attribut (Pos.Mark (a, a_pos), attr) -> ( match expand_access const_map loop_map (Pos.same a m_expr) with @@ -710,7 +713,7 @@ let expand_formula (const_map : const_context) let v' = match expand_variable const_map ParamsMap.empty v with | Pos.Mark (AtomVar m_v, v_pos) -> Pos.mark (Pos.unmark m_v) v_pos - | Pos.Mark (AtomLiteral (Float _), v_pos) -> + | Pos.Mark (AtomLiteral { lit = Float _; _ }, v_pos) -> Err.constant_forbidden_as_lvalue v_pos | _ -> assert false in @@ -736,7 +739,7 @@ let expand_formula (const_map : const_context) let v' = match expand_variable const_map loop_map v with | Pos.Mark (AtomVar m_v, v_pos) -> Pos.mark (Pos.unmark m_v) v_pos - | Pos.Mark (AtomLiteral (Float _), v_pos) -> + | Pos.Mark (AtomLiteral { lit = Float _; _ }, v_pos) -> Err.constant_forbidden_as_lvalue v_pos | _ -> assert false in diff --git a/src/mlang/m_frontend/mast_to_mir.ml b/src/mlang/m_frontend/mast_to_mir.ml index 7fb1f0366..15b466997 100644 --- a/src/mlang/m_frontend/mast_to_mir.ml +++ b/src/mlang/m_frontend/mast_to_mir.ml @@ -658,13 +658,13 @@ let rec translate_expression (p : Validator.program) (dict : Com.Var.t IntMap.t) Attribut (Pos.mark access' pos, a) else match StrMap.find_opt (Pos.unmark a) (Com.Var.attrs var) with - | Some l -> Literal (Float (float (Pos.unmark l))) - | None -> Literal Undefined) + | Some l -> Com.mk_lit (Float (float (Pos.unmark l))) + | None -> Com.mk_lit Undefined) | TabAccess (_, m_id, _) -> ( let var = get_var dict m_id in match StrMap.find_opt (Pos.unmark a) (Com.Var.attrs var) with - | Some l -> Literal (Float (float (Pos.unmark l))) - | None -> Literal Undefined) + | Some l -> Com.mk_lit (Float (float (Pos.unmark l))) + | None -> Com.mk_lit Undefined) | FieldAccess (m_sp_opt, e, f, _) -> let m_sp_opt' = Option.map @@ -684,8 +684,8 @@ let rec translate_expression (p : Validator.program) (dict : Com.Var.t IntMap.t) if Com.Var.is_ref var then let access' = translate_access p dict access in Size (Pos.mark access' pos) - else Literal (Float (float @@ Com.Var.size var)) - | TabAccess _ -> Literal (Float 1.0) + else Com.mk_lit (Float (float @@ Com.Var.size var)) + | TabAccess _ -> Com.mk_lit (Float 1.0) | FieldAccess (m_sp_opt, e, f, _) -> let m_sp_opt' = Option.map @@ -707,11 +707,11 @@ let rec translate_expression (p : Validator.program) (dict : Com.Var.t IntMap.t) IsVariable (Pos.mark access' pos, m_name) else let name = Pos.unmark m_name in - if Com.Var.name_str var = name then Literal (Float 1.0) + if Com.Var.name_str var = name then Com.mk_lit (Float 1.0) else match Com.Var.alias var with - | Some m_a when Pos.unmark m_a = name -> Literal (Float 1.0) - | _ -> Literal (Float 0.0)) + | Some m_a when Pos.unmark m_a = name -> Com.mk_lit (Float 1.0) + | _ -> Com.mk_lit (Float 0.0)) | _ -> let access' = translate_access p dict access in IsVariable (Pos.mark access' pos, m_name)) diff --git a/src/mlang/m_frontend/mparser.mly b/src/mlang/m_frontend/mparser.mly index be48db696..670cc0247 100644 --- a/src/mlang/m_frontend/mparser.mly +++ b/src/mlang/m_frontend/mparser.mly @@ -28,7 +28,7 @@ along with this program. If not, see . let parse_to_atom (v: parse_val) (pos : Pos.t) : Com.m_var_name Com.atom = match v with | ParseVar v -> AtomVar (Pos.mark v pos) - | ParseInt v -> AtomLiteral (Float (float_of_int v)) + | ParseInt v -> Com.mk_atomlit (Float (float_of_int v)) (** Module generated automaticcaly by Menhir, the parser generator *) %} @@ -769,7 +769,7 @@ instruction: let expr = match eo with | Some expr -> expr - | None -> Pos.without (Com.Literal (Com.Float 1.0)) + | None -> Pos.without (Com.mk_lit (Com.Float 1.0)) in Some (ComputeVerifs (dom, expr, m_sp_opt)) } @@ -1007,7 +1007,7 @@ it_param: let expr = match eo with | Some expr -> expr - | None -> Pos.without (Com.Literal (Com.Float 1.0)) + | None -> Pos.without (Com.mk_lit (Com.Float 1.0)) in let m_sp_opt = match spo with Some m_sp -> Some (m_sp, -1) | None -> None in `VarCatsIt (vcats, expr, m_sp_opt) @@ -1059,7 +1059,7 @@ rest_param: let expr = match eo with | Some expr -> expr - | None -> Pos.without (Com.Literal (Com.Float 1.0)) + | None -> Pos.without (Com.mk_lit (Com.Float 1.0)) in let m_sp_opt = match spo with Some m_sp -> Some (m_sp, -1) | None -> None in `VarCatsRest (var, vcats, expr, m_sp_opt) @@ -1434,7 +1434,7 @@ factor: | Com.AtomVar v -> Com.Var (VarAccess (None, v)) | Com.AtomLiteral l -> Com.Literal l } -| UNDEFINED { Com.Literal Undefined } +| UNDEFINED { Com.mk_lit Undefined } | LPAREN e = expression RPAREN { e } loop_expression: diff --git a/src/mlang/m_frontend/parse_utils.ml b/src/mlang/m_frontend/parse_utils.ml index 3b6ac0bcb..143bbdec0 100644 --- a/src/mlang/m_frontend/parse_utils.ml +++ b/src/mlang/m_frontend/parse_utils.ml @@ -88,7 +88,7 @@ let parse_literal sloc (s : string) : Com.literal = with Failure _ -> E.raise_spanned_error "invalid literal" (mk_position sloc) let parse_atom sloc (s : string) : Com.m_var_name Com.atom = - try Com.AtomLiteral (Com.Float (float_of_string s)) + try Com.mk_atomlit (Com.Float (float_of_string s)) with Failure _ -> Com.AtomVar (Pos.mark (parse_variable sloc s) (mk_position sloc)) diff --git a/src/mlang/m_frontend/validator.ml b/src/mlang/m_frontend/validator.ml index 873236dca..16ac13c06 100644 --- a/src/mlang/m_frontend/validator.ml +++ b/src/mlang/m_frontend/validator.ml @@ -2861,8 +2861,8 @@ let eval_expr_verif (prog : program) (verif : verif) in let rec aux expr = match Pos.unmark expr with - | Com.Literal (Com.Float f) -> Some f - | Literal Com.Undefined -> None + | Com.Literal { lit = Com.Float f; _ } -> Some f + | Literal { lit = Com.Undefined; _ } -> None | Var _ -> Err.variable_forbidden_in_filter (Pos.get expr) | Attribut (Pos.Mark (VarAccess (_, m_v), _), m_attr) -> let var_name = Com.get_normal_var @@ Pos.unmark m_v in diff --git a/src/mlang/m_ir/com.ml b/src/mlang/m_ir/com.ml index b7062ee26..81f2b7e14 100644 --- a/src/mlang/m_ir/com.ml +++ b/src/mlang/m_ir/com.ml @@ -432,6 +432,10 @@ type variable_space = { type literal = Float of float | Undefined +type origin = string Pos.marked option + +type literal_with_orig = { lit : literal; origin : origin } + (** Unary operators *) type unop = Not | Minus @@ -476,7 +480,7 @@ type 'v access = and 'v m_access = 'v access Pos.marked -and 'v atom = AtomVar of 'v | AtomLiteral of literal +and 'v atom = AtomVar of 'v | AtomLiteral of literal_with_orig and 'v set_value_loop = | Single of 'v atom Pos.marked @@ -505,7 +509,7 @@ and 'v expression = | FuncCall of func Pos.marked * 'v m_expression list | FuncCallLoop of func Pos.marked * 'v loop_variables Pos.marked * 'v m_expression - | Literal of literal + | Literal of literal_with_orig | Var of 'v access | Loop of 'v loop_variables Pos.marked * 'v m_expression (** The loop is prefixed with the loop variables declarations *) @@ -561,17 +565,27 @@ let get_used_variables (e : 'v expression) : | TabAccess (_, v, m_i) -> (Tab (v, m_i), None) :: acc | VarAccess (_, v) -> (V v, None) :: acc | FieldAccess (_, Mark (v, _), _, _) -> get_used_variables_ v acc) - | Literal lit -> (LiteralDep lit, None) :: acc - (* | Literal { lit; origin = Some (Mark (id, pos)) } -> *) - (* (Const { id; value = lit; pos }, None) :: acc *) - (* | Literal { lit; origin = None } -> *) - (* (LiteralDep lit, None) :: acc *) + | Literal { lit; origin = Some (Mark (id, pos)) } -> + (Const { id; value = lit; pos }, None) :: acc + | Literal { lit; origin = None } -> (LiteralDep lit, None) :: acc | NbCategory _ | NbAnomalies | NbDiscordances | NbInformatives | NbBloquantes -> acc in get_used_variables_ e [] +let mk_lit_with_orig lit origin = { lit; origin } + +let mk_lit lit = Literal (mk_lit_with_orig lit None) + +let mk_lit_from_const lit constname = + Literal (mk_lit_with_orig lit (Some constname)) + +let mk_atomlit lit = AtomLiteral (mk_lit_with_orig lit None) + +let mk_atomlit_from_const lit constname = + AtomLiteral (mk_lit_with_orig lit (Some constname)) + module Error = struct type typ = Anomaly | Discordance | Information @@ -1124,7 +1138,7 @@ let format_literal fmt l = let format_atom form_var fmt vl = match vl with | AtomVar v -> form_var fmt v - | AtomLiteral l -> format_literal fmt l + | AtomLiteral l -> format_literal fmt l.lit let format_set_value_loop form_var fmt sv = let form_atom = format_atom form_var in @@ -1265,7 +1279,7 @@ let rec format_expression form_var fmt = Format.fprintf fmt "%a(%a%a)" format_func (Pos.unmark f) (format_loop_variables form_var) (Pos.unmark lvs) form_expr (Pos.unmark e) - | Literal l -> format_literal fmt l + | Literal { lit; _ } -> format_literal fmt lit | Var acc -> format_access form_var form_expr fmt acc | Loop (lvs, e) -> Format.fprintf fmt "pour %a%a" diff --git a/src/mlang/m_ir/com.mli b/src/mlang/m_ir/com.mli index 1936dbe2b..e5b1e5130 100644 --- a/src/mlang/m_ir/com.mli +++ b/src/mlang/m_ir/com.mli @@ -212,6 +212,10 @@ type verif_domain = verif_domain_data domain type literal = Float of float | Undefined +type origin = string Pos.marked option + +type literal_with_orig = { lit : literal; origin : origin } + (** Unary operators *) type unop = Not | Minus @@ -268,7 +272,7 @@ type 'v access = and 'v m_access = 'v access Pos.marked (** Values that can be substituted for loop parameters *) -and 'v atom = AtomVar of 'v | AtomLiteral of literal +and 'v atom = AtomVar of 'v | AtomLiteral of literal_with_orig and 'v set_value_loop = | Single of 'v atom Pos.marked @@ -301,7 +305,7 @@ and 'v expression = | FuncCall of func Pos.marked * 'v m_expression list | FuncCallLoop of func Pos.marked * 'v loop_variables Pos.marked * 'v m_expression - | Literal of literal + | Literal of literal_with_orig | Var of 'v access | Loop of 'v loop_variables Pos.marked * 'v m_expression (** The loop is prefixed with the loop variables declarations *) @@ -326,6 +330,20 @@ type 'v dep = val get_used_variables : 'v expression -> ('v dep * 'v expression option) list +val mk_atomlit : literal -> 'v atom +(** [mk_atomtit lit] makes a Literal expression with no origin *) + +val mk_atomlit_from_const : literal -> string Pos.marked -> 'v atom +(** [mk_atomlit_from_const] makes a Literal expression with + the name of the const as origin *) + +val mk_lit : literal -> 'v expression +(** [mk_lit lit] makes a Literal expression with no origin *) + +val mk_lit_from_const : literal -> string Pos.marked -> 'v expression +(** [mk_lit_from_const] makes a Literal expression with + the name of the const as origin *) + module Error : sig type typ = Anomaly | Discordance | Information diff --git a/src/mlang/m_ir/mir.ml b/src/mlang/m_ir/mir.ml index 8c8548ac1..da48dd8ee 100644 --- a/src/mlang/m_ir/mir.ml +++ b/src/mlang/m_ir/mir.ml @@ -164,7 +164,7 @@ let rec expand_functions_expr (e : 'var Com.expression Pos.marked) : None args in let expr = - match expr_opt with None -> Literal (Float 0.0) | Some expr -> expr + match expr_opt with None -> Com.mk_lit (Float 0.0) | Some expr -> expr in Pos.same expr e | FuncCall (Pos.Mark (GtzFunc, _), [ arg ]) -> @@ -172,14 +172,14 @@ let rec expand_functions_expr (e : 'var Com.expression Pos.marked) : (Comparison ( Pos.same Com.Gt e, expand_functions_expr arg, - Pos.same (Literal (Float 0.0)) e )) + Pos.same (Com.mk_lit (Float 0.0)) e )) e | FuncCall (Pos.Mark (GtezFunc, _), [ arg ]) -> Pos.same (Comparison ( Pos.same Com.Gte e, expand_functions_expr arg, - Pos.same (Literal (Float 0.0)) e )) + Pos.same (Com.mk_lit (Float 0.0)) e )) e | FuncCall ((Pos.Mark ((MinFunc | MaxFunc), _) as fn), [ arg1; arg2 ]) -> let earg1 = expand_functions_expr arg1 in @@ -192,7 +192,7 @@ let rec expand_functions_expr (e : 'var Com.expression Pos.marked) : (Comparison ( Pos.same Com.Eq e, expand_functions_expr arg, - Pos.same (Literal (Float 0.0)) e )) + Pos.same (Com.mk_lit (Float 0.0)) e )) e | FuncCall (fn, args) -> Pos.same (FuncCall (fn, List.map expand_functions_expr args)) e diff --git a/src/mlang/m_ir/mir_interpreter.ml b/src/mlang/m_ir/mir_interpreter.ml index db2a45fac..be90389ad 100644 --- a/src/mlang/m_ir/mir_interpreter.ml +++ b/src/mlang/m_ir/mir_interpreter.ml @@ -738,8 +738,8 @@ struct | Some e3 -> evaluate_expr ctx e3) | Number _ -> evaluate_expr ctx e2 | Undefined -> Undefined) - | Literal Undefined -> Undefined - | Literal (Float f) -> Number (N.of_float f) + | Literal { lit = Undefined; _ } -> Undefined + | Literal { lit = Float f; _ } -> Number (N.of_float f) | Var access -> get_access_value ctx access | FuncCall (Pos.Mark (ArrFunc, _), [ arg ]) -> ( match evaluate_expr ctx arg with @@ -794,7 +794,7 @@ struct in let access_index (i : int) : Int64.t option = let ei = - Pos.same (Com.Literal (Float (float_of_int i))) arg2 + Pos.same (Com.mk_lit (Float (float_of_int i))) arg2 in let instr = let m_sp_opt = From 710fd6dde9c86ae934d25244914320622243fb1a Mon Sep 17 00:00:00 2001 From: Alexandre Doussot Date: Tue, 2 Dec 2025 15:54:15 +0100 Subject: [PATCH 07/14] add target specification to tracing --- src/mlang/test_framework/test_interpreter.ml | 56 +++++++++++++++---- src/mlang/test_framework/test_interpreter.mli | 10 ++++ src/mlang/utils/config.ml | 4 ++ src/mlang/utils/config.mli | 8 +++ 4 files changed, 68 insertions(+), 10 deletions(-) diff --git a/src/mlang/test_framework/test_interpreter.ml b/src/mlang/test_framework/test_interpreter.ml index b2d8de5c8..03f04647e 100644 --- a/src/mlang/test_framework/test_interpreter.ml +++ b/src/mlang/test_framework/test_interpreter.ml @@ -135,8 +135,11 @@ let to_MIR_function_and_inputs (program : Mir.program) (t : Irj_ast.irj_file) : exception InterpError of int +type target_dbg_info = { target : string; dbg_info : Dbg_info.t } + let check_test (program : Mir.program) (test_name : string) - (value_sort : Config.value_sort) (round_ops : Config.round_ops) : unit = + (value_sort : Config.value_sort) (round_ops : Config.round_ops) : + target_dbg_info list = let check_vars exp vars = let test_error_margin = 0.01 in let fold vname f nb = @@ -180,15 +183,46 @@ let check_test (program : Mir.program) (test_name : string) Cli.debug_print "Running test %s..." t.nom; let insts = to_MIR_function_and_inputs program t in let rec check = function - | [] -> () + | [] -> [] | inst :: insts -> Cli.debug_print "Executing program %s" inst.label; (* Cli.debug_print "Combined Program (w/o verif conds):@.%a@." Format_bir.format_program program; *) - let dbg_info = None in - let varMap, anoSet, _dbg_info = + let dbg_info = Dbg_info.empty in + let add_input_var_to_info var lit dbg_info = + let open Dbg_info in + let name = Com.Var.name_str var in + let pos = Com.Var.name var |> Pos.get in + let origin = + Origin.make (Pos.get_file pos) (Pos.get_start_line pos) + (Pos.get_end_line pos) Origin.Declared + in + let tick = Tick.tick () in + let descr = + match Com.Var.descr_str var with + | exception _ -> None + | descr -> Some descr + in + let runtime = Info.Runtime.make origin lit (Some name) in + let runtimes = Tick.Map.add tick runtime dbg_info.runtimes in + let static = Info.Static.make name origin true descr in + let statics = IntMap.add runtime.hash static dbg_info.statics in + let ledger = StrMap.add name tick dbg_info.ledger in + { dbg_info with runtimes; statics; ledger } + in + let dbg_info = + Com.Var.Map.fold add_input_var_to_info inst.vars dbg_info + in + let varMap, anoSet, dbg_info = Mir_interpreter.evaluate_program program inst.vars inst.events - value_sort round_ops dbg_info + value_sort round_ops (Some dbg_info) + in + let target_dbg_info = + match (!Config.platform, dbg_info) with + | Server _, Some dbg_info -> + let target_dbg_info = { dbg_info; target = inst.label } in + Some target_dbg_info + | _, _ -> None in let nbErrs = check_vars inst.expectedVars varMap @@ -196,14 +230,16 @@ let check_test (program : Mir.program) (test_name : string) in if nbErrs <= 0 then ( Cli.debug_print "OK!"; - check insts) + target_dbg_info :: check insts) else ( Cli.debug_print "KO!"; raise (InterpError nbErrs)) in - check insts; + let infos = check insts in + let clean_infos = List.filter_map (fun i -> i) infos in Config.warning_flag := dbg_warning; - Config.display_time := dbg_time + Config.display_time := dbg_time; + clean_infos type process_acc = string list * int StrMap.t @@ -233,7 +269,7 @@ let check_all_tests (p : Mir.program) (test_dir : string) in try Config.debug_flag := false; - check_test p (test_dir ^ name) value_sort round_ops; + ignore @@ check_test p (test_dir ^ name) value_sort round_ops; Config.debug_flag := true; Cli.result_print "%s" name; (name :: successes, failures) @@ -294,7 +330,7 @@ let check_one_test (p : Mir.program) (name : string) in try Config.debug_flag := false; - check_test p name value_sort round_ops; + ignore @@ check_test p name value_sort round_ops; Config.debug_flag := true; Cli.result_print "%s" name; None diff --git a/src/mlang/test_framework/test_interpreter.mli b/src/mlang/test_framework/test_interpreter.mli index 31531b0e6..140599288 100644 --- a/src/mlang/test_framework/test_interpreter.mli +++ b/src/mlang/test_framework/test_interpreter.mli @@ -12,6 +12,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *) + +type target_dbg_info = { target : string; dbg_info : Dbg_info.t } + val check_all_tests : Mir.program -> string -> @@ -22,6 +25,13 @@ val check_all_tests : (** [check_all_tests p folder vs ro filter] Executes [p] with all tests in [folder] whose name satisfy [filter]. *) +val check_test : + Mir.program -> + string -> + Config.value_sort -> + Config.round_ops -> + target_dbg_info list + val check_one_test : Mir.program -> string -> Config.value_sort -> Config.round_ops -> unit (** Same as [check_all_tests], but for one test. *) diff --git a/src/mlang/utils/config.ml b/src/mlang/utils/config.ml index b608140e7..19c26bc9e 100644 --- a/src/mlang/utils/config.ml +++ b/src/mlang/utils/config.ml @@ -84,6 +84,8 @@ type execution_mode = type files = NonEmpty of string list +type platform = Executable | Server of string StrMap.t + (* Flags inherited from the old compiler *) let get_files = function NonEmpty l -> l @@ -135,6 +137,8 @@ let comparison_error_margin = ref 0.000001 let income_year = ref 0 +let platform = ref Executable + let set_all_arg_refs (files_ : files) applications_ (without_dgfip_m_ : bool) (debug_ : bool) (var_info_debug_ : string list) (display_time_ : bool) (no_print_cycles_ : bool) (output_file_ : string option) diff --git a/src/mlang/utils/config.mli b/src/mlang/utils/config.mli index 0599477bf..39373bf8a 100644 --- a/src/mlang/utils/config.mli +++ b/src/mlang/utils/config.mli @@ -60,6 +60,12 @@ type execution_mode = type files = NonEmpty of string list +type platform = + | Executable + | Server of string StrMap.t + (** This type represents how the interpreter is run. By default, it's as an + Executable *) + val get_files : files -> string list val source_files : files ref @@ -118,6 +124,8 @@ val dgfip_flags : Dgfip_options.flags ref val execution_mode : execution_mode ref +val platform : platform ref + val set_all_arg_refs : (* files *) files -> (* applications *) string list -> From 869d0d5c8e3e326b294230739b31ffd0be81df43 Mon Sep 17 00:00:00 2001 From: Alexandre Doussot Date: Wed, 8 Oct 2025 17:04:30 +0200 Subject: [PATCH 08/14] Split Driver and Parsing --- src/mlang/driver.ml | 118 +-------------------------- src/mlang/m_frontend/parse_utils.ml | 4 + src/mlang/m_frontend/parse_utils.mli | 4 + src/mlang/parsing.ml | 108 ++++++++++++++++++++++++ 4 files changed, 119 insertions(+), 115 deletions(-) create mode 100644 src/mlang/parsing.ml diff --git a/src/mlang/driver.ml b/src/mlang/driver.ml index e96698374..46e202b81 100644 --- a/src/mlang/driver.ml +++ b/src/mlang/driver.ml @@ -14,9 +14,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *) -open Lexing -open Mlexer - exception Exit let process_dgfip_options (backend : Config.backend) @@ -40,117 +37,6 @@ let process_dgfip_options (backend : Config.backend) end | UnknownBackend -> Config.Dgfip_options.default_flags -(* The legacy compiler plays a nasty trick on us, that we have to reproduce: - rule 1 is modified to add assignments to APPLI_XXX variables according to the - target application (OCEANS, BATCH and ILIAD). *) -let patch_rule_1 (backend : Config.backend) - (dgfip_flags : Config.Dgfip_options.flags) (program : Mast.program) : - Mast.program = - let open Mast in - let var_exists name = - List.exists - (List.exists (fun m_item -> - match Pos.unmark m_item with - | VariableDecl (ComputedVar m_cv) -> - Pos.unmark (Pos.unmark m_cv).comp_name = name - | VariableDecl (InputVar m_iv) -> - Pos.unmark (Pos.unmark m_iv).input_name = name - | _ -> false)) - program - in - let mk_assign name value l = - if var_exists name then - let m_access = - Pos.without (Com.VarAccess (None, Pos.without (Com.Normal name))) - in - let litt = Com.mk_lit (Com.Float (if value then 1.0 else 0.0)) in - let cmd = Com.SingleFormula (VarDecl (m_access, Pos.without litt)) in - Pos.without cmd :: l - else l - in - let oceans, batch, iliad = - match backend with - | Dgfip_c -> - (dgfip_flags.flg_cfir, dgfip_flags.flg_gcos, dgfip_flags.flg_iliad) - | UnknownBackend -> (false, false, true) - in - List.map - (List.map (fun m_item -> - match Pos.unmark m_item with - | Rule r when Pos.unmark r.rule_number = 1 -> - let fl = - List.map - (fun f -> Pos.same (Com.Affectation f) f) - ([] - |> mk_assign "APPLI_OCEANS" oceans - |> mk_assign "APPLI_BATCH" batch - |> mk_assign "APPLI_ILIAD" iliad) - in - let r' = { r with rule_formulaes = r.rule_formulaes @ fl } in - Pos.same (Rule r') m_item - | _ -> m_item)) - program - -let parse () = - let current_progress, finish = Cli.create_progress_bar "Parsing" in - - let parse filebuf source_file = - current_progress source_file; - let lex_curr_p = { filebuf.lex_curr_p with pos_fname = source_file } in - let filebuf = { filebuf with lex_curr_p } in - match Mparser.source_file token filebuf with - | commands -> commands - | exception Mparser.Error -> - Errors.raise_spanned_error "M syntax error" - (Parse_utils.mk_position (filebuf.lex_start_p, filebuf.lex_curr_p)) - in - - let parse_file source_file = - let input = open_in source_file in - let filebuf = Lexing.from_channel input in - try - parse filebuf source_file - (* We're catching exceptions to properly close the input channel *) - with Errors.StructuredError _ as e -> - close_in input; - raise e - in - - let parse_m_dgfip m_program = - if !Config.without_dgfip_m then m_program - else - let parse_internal str = - let filebuf = Lexing.from_string str in - let source_file = Dgfip_m.internal_m in - parse filebuf source_file - in - let decs = parse_internal Dgfip_m.declarations in - let events = parse_internal Dgfip_m.event_declaration in - events :: decs :: m_program - in - - let parse_m_files m_program = - let parse_file_progress source_file = - current_progress source_file; - parse_file source_file - in - (*FIXME: use a fold here *) - let prog = - List.map parse_file_progress @@ Config.get_files !Config.source_files - in - List.rev prog @ m_program - in - - let m_program = - [] |> parse_m_dgfip |> parse_m_files |> List.rev - |> patch_rule_1 !Config.backend !Config.dgfip_flags - in - finish "completed!"; - m_program - -(** Entry function for the executable. Returns a negative number in case of - error. *) - let set_opts (files : string list) (application_names : string list) (without_dgfip_m : bool) (debug : bool) (var_info_debug : string list) (display_time : bool) (print_cycles : bool) (backend : string option) @@ -257,7 +143,9 @@ let extract m_program = let driver () = try Cli.debug_print "Reading M files..."; - let m_program = parse () in + let progress_bar = Cli.create_progress_bar "Parsing" in + let files = Config.get_files !Config.source_files in + let m_program = Parsing.parse files progress_bar in Cli.debug_print "Elaborating..."; let m_program = Expander.proceed m_program in let m_program = Validator.proceed !Config.mpp_function m_program in diff --git a/src/mlang/m_frontend/parse_utils.ml b/src/mlang/m_frontend/parse_utils.ml index 143bbdec0..f58ede8ad 100644 --- a/src/mlang/m_frontend/parse_utils.ml +++ b/src/mlang/m_frontend/parse_utils.ml @@ -16,8 +16,12 @@ module E = Errors +type loc = Lexing.position * Lexing.position + let mk_position sloc = Pos.make (fst sloc).Lexing.pos_fname sloc +let make_loc loc = loc + (** {1 Frontend variable names}*) let parse_variable_name sloc (s : string) : string = diff --git a/src/mlang/m_frontend/parse_utils.mli b/src/mlang/m_frontend/parse_utils.mli index 186ebc546..cd157a683 100644 --- a/src/mlang/m_frontend/parse_utils.mli +++ b/src/mlang/m_frontend/parse_utils.mli @@ -21,8 +21,12 @@ (** A parsed variable can be a regular variable or an integer literal *) type parse_val = ParseVar of Com.var_name | ParseInt of int +type loc = Lexing.position * Lexing.position + val mk_position : Lexing.position * Lexing.position -> Pos.t +val make_loc : loc -> loc + val parse_variable : Lexing.position * Lexing.position -> string -> Com.var_name (** Checks whether the variable contains parameters *) diff --git a/src/mlang/parsing.ml b/src/mlang/parsing.ml new file mode 100644 index 000000000..64820ffd9 --- /dev/null +++ b/src/mlang/parsing.ml @@ -0,0 +1,108 @@ +open Lexing +open Mlexer + +(* The legacy compiler plays a nasty trick on us, that we have to reproduce: + rule 1 is modified to add assignments to APPLI_XXX variables according to the + target application (OCEANS, BATCH and ILIAD). *) +let patch_rule_1 (backend : Config.backend) + (dgfip_flags : Config.Dgfip_options.flags) (program : Mast.program) : + Mast.program = + let open Mast in + let var_exists name = + List.exists + (List.exists (fun m_item -> + match Pos.unmark m_item with + | VariableDecl (ComputedVar m_cv) -> + Pos.unmark (Pos.unmark m_cv).comp_name = name + | VariableDecl (InputVar m_iv) -> + Pos.unmark (Pos.unmark m_iv).input_name = name + | _ -> false)) + program + in + let mk_assign name value l = + if var_exists name then + let m_access = + Pos.without (Com.VarAccess (None, Pos.without (Com.Normal name))) + in + let litt = Com.mk_lit (Com.Float (if value then 1.0 else 0.0)) in + let cmd = Com.SingleFormula (VarDecl (m_access, Pos.without litt)) in + Pos.without cmd :: l + else l + in + let oceans, batch, iliad = + match backend with + | Dgfip_c -> + (dgfip_flags.flg_cfir, dgfip_flags.flg_gcos, dgfip_flags.flg_iliad) + | UnknownBackend -> (false, false, true) + in + List.map + (List.map (fun m_item -> + match Pos.unmark m_item with + | Rule r when Pos.unmark r.rule_number = 1 -> + let fl = + List.map + (fun f -> Pos.same (Com.Affectation f) f) + ([] + |> mk_assign "APPLI_OCEANS" oceans + |> mk_assign "APPLI_BATCH" batch + |> mk_assign "APPLI_ILIAD" iliad) + in + let r' = { r with rule_formulaes = r.rule_formulaes @ fl } in + Pos.same (Rule r') m_item + | _ -> m_item)) + program + +(** Entry function for the executable. Returns a negative number in case of + error. *) +let parse_lexbuf filebuf source_file = + let lex_curr_p = { filebuf.lex_curr_p with pos_fname = source_file } in + let filebuf = { filebuf with lex_curr_p } in + match Mparser.source_file token filebuf with + | commands -> commands + | exception Mparser.Error -> + let loc = + Parse_utils.make_loc (filebuf.lex_start_p, filebuf.lex_curr_p) + in + Errors.raise_spanned_error "M syntax error" (Parse_utils.mk_position loc) + +let parse_file source_file = + let input = open_in source_file in + let filebuf = Lexing.from_channel input in + try + parse_lexbuf filebuf source_file + (* We're catching exceptions to properly close the input channel *) + with Errors.StructuredError _ as e -> + close_in input; + raise e + +let parse_m_dgfip current_progress m_program = + let parse_internal str = + let filebuf = Lexing.from_string str in + let source_file = Dgfip_m.internal_m in + current_progress source_file; + parse_lexbuf filebuf source_file + in + let decs = parse_internal Dgfip_m.declarations in + let events = parse_internal Dgfip_m.event_declaration in + events :: decs :: m_program + +let parse_m_files files current_progress m_program = + let parse_file_progress source_file = + current_progress source_file; + parse_file source_file + in + (*FIXME: use a fold here *) + let prog = List.map parse_file_progress files in + List.rev prog @ m_program + +let parse files progress_bar = + let current_progress, finish = progress_bar in + let m_program = + [] + |> parse_m_dgfip current_progress + |> parse_m_files files current_progress + |> List.rev + |> patch_rule_1 !Config.backend !Config.dgfip_flags + in + finish "completed!"; + m_program From e8404651464edc469decdcb7e831083c6aec133b Mon Sep 17 00:00:00 2001 From: Alexandre Doussot Date: Mon, 17 Nov 2025 12:31:11 +0100 Subject: [PATCH 09/14] Add plain output flag --- src/mlang/utils/cli.ml | 14 +++++++++++--- src/mlang/utils/cli.mli | 1 + src/mlang/utils/config.ml | 6 +++++- src/mlang/utils/config.mli | 3 +++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/mlang/utils/cli.ml b/src/mlang/utils/cli.ml index f97601372..97cb253d3 100644 --- a/src/mlang/utils/cli.ml +++ b/src/mlang/utils/cli.ml @@ -137,6 +137,14 @@ let roundops = running on a mainframe. In this case, the size of the long type has \ to be specified; it can be either 32 or 64.") +let plain_output = + Arg.( + value + & flag + & info + [ "plain_output" ] + ~doc:"Do not print terminal characters.") + let comparison_error_margin_cli = Arg.( value @@ -183,7 +191,7 @@ let mlang_t f = $ display_time $ no_print_cycles $ backend $ output $ run_all_tests $ dgfip_test_filter $ run_test $ mpp_function $ optimize_unsafe_float $ precision $ roundops $ comparison_error_margin_cli $ income_year_cli - $ m_clean_calls $ dgfip_options) + $ m_clean_calls $ dgfip_options $ plain_output) let info = let doc = @@ -273,8 +281,8 @@ let time_marker () = let format_with_style (styles : ANSITerminal.style list) (str : ('a, unit, string) format) = - if true (* can depend on a stylr flag *) then ANSITerminal.sprintf styles str - else Printf.sprintf str + if !Config.plain_output (* can depend on a stylr flag *) then Printf.sprintf str + else ANSITerminal.sprintf styles str (** Prints [\[DEBUG\]] in purple on the terminal standard output as well as timing since last debug *) diff --git a/src/mlang/utils/cli.mli b/src/mlang/utils/cli.mli index bfeb9c31d..267fb361a 100644 --- a/src/mlang/utils/cli.mli +++ b/src/mlang/utils/cli.mli @@ -39,6 +39,7 @@ val mlang_t : int -> bool -> string list option -> + bool -> 'a) -> 'a Cmdliner.Term.t (** Mlang binary command-line arguments parsing function *) diff --git a/src/mlang/utils/config.ml b/src/mlang/utils/config.ml index 19c26bc9e..568db3e6c 100644 --- a/src/mlang/utils/config.ml +++ b/src/mlang/utils/config.ml @@ -139,6 +139,8 @@ let income_year = ref 0 let platform = ref Executable +let plain_output = ref true + let set_all_arg_refs (files_ : files) applications_ (without_dgfip_m_ : bool) (debug_ : bool) (var_info_debug_ : string list) (display_time_ : bool) (no_print_cycles_ : bool) (output_file_ : string option) @@ -146,7 +148,8 @@ let set_all_arg_refs (files_ : files) applications_ (without_dgfip_m_ : bool) (comparison_error_margin_ : float option) (income_year_ : int) (value_sort_ : value_sort) (round_ops_ : round_ops) (backend_ : backend) (dgfip_test_filter_ : bool) (mpp_function_ : string) - (dgfip_flags_ : Dgfip_options.flags) (execution_mode_ : execution_mode) = + (dgfip_flags_ : Dgfip_options.flags) (execution_mode_ : execution_mode) + (plain_output_ : bool) = source_files := files_; application_names := applications_; without_dgfip_m := without_dgfip_m_; @@ -165,6 +168,7 @@ let set_all_arg_refs (files_ : files) applications_ (without_dgfip_m_ : bool) dgfip_test_filter := dgfip_test_filter_; mpp_function := mpp_function_; dgfip_flags := dgfip_flags_; + plain_output := plain_output_; match output_file_ with | None -> () | Some o -> ( diff --git a/src/mlang/utils/config.mli b/src/mlang/utils/config.mli index 39373bf8a..5024f1d98 100644 --- a/src/mlang/utils/config.mli +++ b/src/mlang/utils/config.mli @@ -126,6 +126,8 @@ val execution_mode : execution_mode ref val platform : platform ref +val plain_output : bool ref + val set_all_arg_refs : (* files *) files -> (* applications *) string list -> @@ -146,4 +148,5 @@ val set_all_arg_refs : (* mpp_function *) string -> (* dgfip_flags *) Dgfip_options.flags -> (* execution_mode *) execution_mode -> + (* plain_output *) bool -> unit From 017fb3d7d0da7042743752fc58c08ea90dc4976e Mon Sep 17 00:00:00 2001 From: Alexandre Doussot Date: Tue, 2 Dec 2025 16:58:51 +0100 Subject: [PATCH 10/14] Make pos not dependent on cli --- src/mlang/utils/cli.ml | 106 ++++++++++++++++++++++++++++++-- src/mlang/utils/cli.mli | 6 ++ src/mlang/utils/errors.ml | 2 +- src/mlang/utils/file.ml | 50 +++++++++++++++ src/mlang/utils/pos.ml | 124 +------------------------------------- src/mlang/utils/pos.mli | 10 --- 6 files changed, 160 insertions(+), 138 deletions(-) create mode 100644 src/mlang/utils/file.ml diff --git a/src/mlang/utils/cli.ml b/src/mlang/utils/cli.ml index 97cb253d3..b965f4aff 100644 --- a/src/mlang/utils/cli.ml +++ b/src/mlang/utils/cli.ml @@ -139,11 +139,8 @@ let roundops = let plain_output = Arg.( - value - & flag - & info - [ "plain_output" ] - ~doc:"Do not print terminal characters.") + value & flag + & info [ "plain_output" ] ~doc:"Do not print terminal characters.") let comparison_error_margin_cli = Arg.( @@ -281,7 +278,8 @@ let time_marker () = let format_with_style (styles : ANSITerminal.style list) (str : ('a, unit, string) format) = - if !Config.plain_output (* can depend on a stylr flag *) then Printf.sprintf str + if !Config.plain_output (* can depend on a stylr flag *) then + Printf.sprintf str else ANSITerminal.sprintf styles str (** Prints [\[DEBUG\]] in purple on the terminal standard output as well as @@ -386,3 +384,99 @@ let result_print kont = Format.kasprintf (fun str -> Format.printf "%a%s@." (fun _ -> result_marker) () str) kont + +let indent_number (s : string) : int = + try + let rec aux (i : int) = if s.[i] = ' ' then aux (i + 1) else i in + aux 0 + with Invalid_argument _ -> String.length s + +let format_matched_line pos (line : string) (line_no : int) : string = + let line_indent = indent_number line in + let error_indicator_style = [ ANSITerminal.red; ANSITerminal.Bold ] in + let sline = Pos.get_start_line pos in + let eline = Pos.get_end_line pos in + let line_start_col = + if line_no = sline then Pos.get_start_column pos else 1 + in + let line_end_col = + if line_no = eline then Pos.get_end_column pos else String.length line + 1 + in + let line_length = String.length line + 1 in + line + ^ + if line_no >= sline && line_no <= eline then + "\n" + ^ + if line_no = sline && line_no = eline then + format_with_style error_indicator_style "%*s" (line_end_col - 1) + (String.make (line_end_col - line_start_col) '^') + else if line_no = sline && line_no <> eline then + format_with_style error_indicator_style "%*s" (line_length - 1) + (String.make (line_length - line_start_col) '^') + else if line_no <> sline && line_no <> eline then + format_with_style error_indicator_style "%*s%s" line_indent "" + (String.make (line_length - line_indent) '^') + else if line_no <> sline && line_no = eline then + format_with_style error_indicator_style "%*s%*s" line_indent "" + (line_end_col - 1 - line_indent) + (String.make (line_end_col - line_indent) '^') + else assert false (* should not happen *) + else "" + +let format_lines pos lines = + let filename = Pos.get_file pos in + let sline = Pos.get_start_line pos in + let eline = Pos.get_end_line pos in + let blue_style = [ ANSITerminal.Bold; ANSITerminal.blue ] in + let spaces = int_of_float (log10 (float_of_int eline)) + 1 in + let lines = + List.mapi (fun i line -> format_matched_line pos line (i + sline)) lines + in + format_with_style blue_style "%*s--> %s\n%s" spaces "" filename + (add_prefix_to_each_line + (Printf.sprintf "\n%s" (String.concat "\n" lines)) + (fun i -> + let cur_line = sline + i - 1 in + if + cur_line >= sline + && cur_line <= sline + (2 * (eline - sline)) + && cur_line mod 2 = sline mod 2 + then + format_with_style blue_style "%*d | " spaces + (sline + ((cur_line - sline) / 2)) + else if cur_line >= sline && cur_line < sline then + format_with_style blue_style "%*d | " spaces cur_line + else if + cur_line <= sline + (2 * (eline - sline)) + 1 + && cur_line > sline + (2 * (eline - sline)) + 1 + then + format_with_style blue_style "%*d | " spaces + (cur_line - (eline - sline + 1)) + else format_with_style blue_style "%*s | " spaces "")) + +let retrieve_loc_text (pos : Pos.t) : string = + let filename = Pos.get_file pos in + if filename = "" then "No position information" + else + let lines = + match !Config.platform with + | Server filemap -> begin + match StrMap.find_opt filename filemap with + | None -> failwith "Pos error" + | Some contents -> + let lines = String.split_on_char '\n' contents in + [ List.nth lines (Pos.get_start_line pos - 1) ] + end + | Executable -> + let get_lines = + match File.open_file_for_text_extraction pos with + | exception Sys_error _ -> + error_print "File not found for displaying position : \"%s\"" + filename; + failwith "Pos error" + | get_lines -> get_lines + in + get_lines 1 + in + format_lines pos lines diff --git a/src/mlang/utils/cli.mli b/src/mlang/utils/cli.mli index 267fb361a..b288fecaf 100644 --- a/src/mlang/utils/cli.mli +++ b/src/mlang/utils/cli.mli @@ -73,3 +73,9 @@ val create_progress_bar : string -> (string -> unit) * (string -> unit) (** Returns two functions: the first one, [current_progress], has to be called during the progress loop and the other one, [finish], has to be called at the end of the progressive task. *) + +val retrieve_loc_text : Pos.t -> string +(** [retrieve_loc_text pos] reads the source file associated with [pos] and + returns a formatted string of the code at that location, with the exact + columns highlighted. This is used to display code snippets in error + messages. *) diff --git a/src/mlang/utils/errors.ml b/src/mlang/utils/errors.ml index feffb2c51..dc2ea807c 100644 --- a/src/mlang/utils/errors.ml +++ b/src/mlang/utils/errors.ml @@ -27,7 +27,7 @@ let format_structured_error fmt (fun (msg, pos) -> Printf.sprintf "%s%s" (match msg with None -> "" | Some msg -> msg ^ "\n") - (Pos.retrieve_loc_text pos)) + (Cli.retrieve_loc_text pos)) pos)) (if List.length pos = 0 then "" else "\n") diff --git a/src/mlang/utils/file.ml b/src/mlang/utils/file.ml new file mode 100644 index 000000000..da1a34e0d --- /dev/null +++ b/src/mlang/utils/file.ml @@ -0,0 +1,50 @@ +let open_file_for_text_extraction (pos : Pos.t) = + let filename = Pos.get_file pos in + let sline = Pos.get_start_line pos in + let eline = Pos.get_end_line pos in + let oc, input_line_opt = + if filename == Dgfip_m.internal_m then + let input_line_opt : unit -> string option = + let curr = ref 0 in + let src = Dgfip_m.declarations in + let lng = String.length src in + let rec new_curr () = + if !curr < lng then + if src.[!curr] = '\n' then ( + let res = !curr in + incr curr; + Some res) + else ( + incr curr; + new_curr ()) + else None + in + function + | () -> ( + let p0 = !curr in + match new_curr () with + | None -> None + | Some p1 -> Some (String.sub Dgfip_m.declarations p0 (p1 - p0))) + in + (None, input_line_opt) + else + let ocf = open_in filename in + let input_line_opt () : string option = + try Some (input_line ocf) with End_of_file -> None + in + (Some ocf, input_line_opt) + in + let rec get_lines (n : int) : string list = + match input_line_opt () with + | Some line -> + if n < sline then get_lines (n + 1) + else if n >= sline && n <= eline then line :: get_lines (n + 1) + else [] + | None -> ( + match oc with + | Some ocf -> + close_in ocf; + [] + | _ -> []) + in + get_lines diff --git a/src/mlang/utils/pos.ml b/src/mlang/utils/pos.ml index f7b8dc9d3..9842836a5 100644 --- a/src/mlang/utils/pos.ml +++ b/src/mlang/utils/pos.ml @@ -15,6 +15,8 @@ (** {1 Source code position} *) +exception ConflictingFilenames of string * string + type t = { pos_filename : string; pos_loc : Lexing.position * Lexing.position } (** A position in the source code is a file, as well as begin and end location of the form col:line *) @@ -24,9 +26,7 @@ let make (f : string) (loc : Lexing.position * Lexing.position) = let make_between (p1 : t) (p2 : t) : t = if p1.pos_filename <> p2.pos_filename then begin - Cli.error_print "Conflicting position filenames: %s <> %s" p1.pos_filename - p2.pos_filename; - failwith "Pos error" + raise @@ ConflictingFilenames (p1.pos_filename, p2.pos_filename) end else let b1, e1 = p1.pos_loc in @@ -127,121 +127,3 @@ let get_end_column (pos : t) : int = e.Lexing.pos_cnum - e.Lexing.pos_bol + 1 let get_file (pos : t) : string = (fst pos.pos_loc).Lexing.pos_fname - -let indent_number (s : string) : int = - try - let rec aux (i : int) = if s.[i] = ' ' then aux (i + 1) else i in - aux 0 - with Invalid_argument _ -> String.length s - -let retrieve_loc_text (pos : t) : string = - let filename = get_file pos in - let blue_style = [ ANSITerminal.Bold; ANSITerminal.blue ] in - if filename = "" then "No position information" - else - let sline = get_start_line pos in - let eline = get_end_line pos in - let oc, input_line_opt = - try - if filename == Dgfip_m.internal_m then - let input_line_opt : unit -> string option = - let curr = ref 0 in - let src = Dgfip_m.declarations in - let lng = String.length src in - let rec new_curr () = - if !curr < lng then - if src.[!curr] = '\n' then ( - let res = !curr in - incr curr; - Some res) - else ( - incr curr; - new_curr ()) - else None - in - function - | () -> ( - let p0 = !curr in - match new_curr () with - | None -> None - | Some p1 -> Some (String.sub Dgfip_m.declarations p0 (p1 - p0)) - ) - in - (None, input_line_opt) - else - let ocf = open_in filename in - let input_line_opt () : string option = - try Some (input_line ocf) with End_of_file -> None - in - (Some ocf, input_line_opt) - with Sys_error _ -> - Cli.error_print "File not found for displaying position : \"%s\"" - filename; - failwith "Pos error" - in - let print_matched_line (line : string) (line_no : int) : string = - let line_indent = indent_number line in - let error_indicator_style = [ ANSITerminal.red; ANSITerminal.Bold ] in - let line_start_col = - if line_no = sline then get_start_column pos else 1 - in - let line_end_col = - if line_no = eline then get_end_column pos else String.length line + 1 - in - let line_length = String.length line + 1 in - line - ^ - if line_no >= sline && line_no <= eline then - "\n" - ^ - if line_no = sline && line_no = eline then - Cli.format_with_style error_indicator_style "%*s" (line_end_col - 1) - (String.make (line_end_col - line_start_col) '^') - else if line_no = sline && line_no <> eline then - Cli.format_with_style error_indicator_style "%*s" (line_length - 1) - (String.make (line_length - line_start_col) '^') - else if line_no <> sline && line_no <> eline then - Cli.format_with_style error_indicator_style "%*s%s" line_indent "" - (String.make (line_length - line_indent) '^') - else if line_no <> sline && line_no = eline then - Cli.format_with_style error_indicator_style "%*s%*s" line_indent "" - (line_end_col - 1 - line_indent) - (String.make (line_end_col - line_indent) '^') - else assert false (* should not happen *) - else "" - in - let include_extra_count = 0 in - let rec get_lines (n : int) : string list = - match input_line_opt () with - | Some line -> - if n < sline - include_extra_count then get_lines (n + 1) - else if - n >= sline - include_extra_count && n <= eline + include_extra_count - then print_matched_line line n :: get_lines (n + 1) - else [] - | None -> [] - in - let pos_lines = get_lines 1 in - let spaces = int_of_float (log10 (float_of_int eline)) + 1 in - (match oc with Some ocf -> close_in ocf | _ -> ()); - Cli.format_with_style blue_style "%*s--> %s\n%s" spaces "" filename - (Cli.add_prefix_to_each_line - (Printf.sprintf "\n%s" (String.concat "\n" pos_lines)) - (fun i -> - let cur_line = sline - include_extra_count + i - 1 in - if - cur_line >= sline - && cur_line <= sline + (2 * (eline - sline)) - && cur_line mod 2 = sline mod 2 - then - Cli.format_with_style blue_style "%*d | " spaces - (sline + ((cur_line - sline) / 2)) - else if cur_line >= sline - include_extra_count && cur_line < sline - then Cli.format_with_style blue_style "%*d | " spaces cur_line - else if - cur_line <= sline + (2 * (eline - sline)) + 1 + include_extra_count - && cur_line > sline + (2 * (eline - sline)) + 1 - then - Cli.format_with_style blue_style "%*d | " spaces - (cur_line - (eline - sline + 1)) - else Cli.format_with_style blue_style "%*s | " spaces "")) diff --git a/src/mlang/utils/pos.mli b/src/mlang/utils/pos.mli index ce99340bf..bd43c80e3 100644 --- a/src/mlang/utils/pos.mli +++ b/src/mlang/utils/pos.mli @@ -55,12 +55,6 @@ val format : Format.formatter -> t -> unit position to the formatter [ppf]. Example: `in file foo.ml, from 10:5 to 12:20`. *) -val retrieve_loc_text : t -> string -(** [retrieve_loc_text pos] reads the source file associated with [pos] and - returns a formatted string of the code at that location, with the exact - columns highlighted. This is used to display code snippets in error - messages. *) - (** {2 Marked Value Manipulators} *) val none : t @@ -114,7 +108,3 @@ val get_file : t -> string (** [get_file pos] returns the filename associated with the position. *) (** {2 Helpers} *) - -val indent_number : string -> int -(** [indent_number s] returns the number of leading space characters in the - string [s]. *) From 473d2e75db59d40b6a3e619ca27fcfba27a80ba4 Mon Sep 17 00:00:00 2001 From: Alexandre Doussot Date: Tue, 2 Dec 2025 16:26:40 +0100 Subject: [PATCH 11/14] Add IRJ parsing for server mode --- src/irj_checker/irj_checker.ml | 2 +- src/mlang/test_framework/irj_file.ml | 15 ++++++++++----- src/mlang/test_framework/irj_file.mli | 4 +++- src/mlang/test_framework/test_interpreter.ml | 12 +++++++----- src/mlang/test_framework/test_interpreter.mli | 4 +++- 5 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/irj_checker/irj_checker.ml b/src/irj_checker/irj_checker.ml index 6d7cbc640..9663e07d6 100644 --- a/src/irj_checker/irj_checker.ml +++ b/src/irj_checker/irj_checker.ml @@ -43,7 +43,7 @@ let irj_checker (f : string) (message_format : message_format_enum) if not (Sys.file_exists f && not (Sys.is_directory f)) then Errors.raise_error (Format.asprintf "%s: this path is not a valid file in the filesystem" f); - let test_data = Mlang.Irj_file.parse_file f in + let test_data = Mlang.Irj_file.parse_file (Filename f) in let test_data = match validation_mode with | Primitive -> diff --git a/src/mlang/test_framework/irj_file.ml b/src/mlang/test_framework/irj_file.ml index 04d053227..c0f5a2097 100644 --- a/src/mlang/test_framework/irj_file.ml +++ b/src/mlang/test_framework/irj_file.ml @@ -16,6 +16,8 @@ open Irj_ast +type input = Filename of string | Contents of string + (* Implement a parsing error handling following François Pottier’s example in https://gitlab.inria.fr/fpottier/menhir/blob/master/demos/calc-syntax-errors/calc.ml *) @@ -85,12 +87,15 @@ let fail text buffer (checkpoint : _ Irj_parser.MenhirInterpreter.checkpoint) = Errors.raise_spanned_error indication (mk_position (MenhirLib.ErrorReports.last buffer)) -let parse_file (test_name : string) : Irj_ast.irj_file = +let parse_file (test_name : input) : Irj_ast.irj_file = let text, filebuf = - try MenhirLib.LexerUtil.read test_name - with Sys_error msg -> - Errors.raise_error - (Format.asprintf "Unable to open file %s (%s)" test_name msg) + match test_name with + | Contents contents -> (contents, Lexing.from_string contents) + | Filename filename -> ( + try MenhirLib.LexerUtil.read filename + with Sys_error msg -> + Errors.raise_error + (Format.asprintf "Unable to open file %s (%s)" filename msg)) in let supplier = Irj_parser.MenhirInterpreter.lexer_lexbuf_to_supplier Irj_lexer.token diff --git a/src/mlang/test_framework/irj_file.mli b/src/mlang/test_framework/irj_file.mli index c0dab0c7f..0f6d831a2 100644 --- a/src/mlang/test_framework/irj_file.mli +++ b/src/mlang/test_framework/irj_file.mli @@ -14,6 +14,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *) -val parse_file : string -> Irj_ast.irj_file +type input = Filename of string | Contents of string + +val parse_file : input -> Irj_ast.irj_file (** [parse_file file] loads the content of a given IRJ [file] in a simple datastructure. *) diff --git a/src/mlang/test_framework/test_interpreter.ml b/src/mlang/test_framework/test_interpreter.ml index 03f04647e..b1642c716 100644 --- a/src/mlang/test_framework/test_interpreter.ml +++ b/src/mlang/test_framework/test_interpreter.ml @@ -137,7 +137,7 @@ exception InterpError of int type target_dbg_info = { target : string; dbg_info : Dbg_info.t } -let check_test (program : Mir.program) (test_name : string) +let check_test (program : Mir.program) (test_input : Irj_file.input) (value_sort : Config.value_sort) (round_ops : Config.round_ops) : target_dbg_info list = let check_vars exp vars = @@ -178,8 +178,9 @@ let check_test (program : Mir.program) (test_name : string) let dbg_time = !Config.display_time in Config.warning_flag := false; Config.display_time := false; - Cli.debug_print "Parsing %s..." test_name; - let t = Irj_file.parse_file test_name in + Cli.debug_print "Parsing %s..." + (match test_input with Filename s -> s | Contents _ -> "given contents"); + let t = Irj_file.parse_file test_input in Cli.debug_print "Running test %s..." t.nom; let insts = to_MIR_function_and_inputs program t in let rec check = function @@ -269,7 +270,8 @@ let check_all_tests (p : Mir.program) (test_dir : string) in try Config.debug_flag := false; - ignore @@ check_test p (test_dir ^ name) value_sort round_ops; + let file = Irj_file.Filename (test_dir ^ name) in + ignore @@ check_test p file value_sort round_ops; Config.debug_flag := true; Cli.result_print "%s" name; (name :: successes, failures) @@ -330,7 +332,7 @@ let check_one_test (p : Mir.program) (name : string) in try Config.debug_flag := false; - ignore @@ check_test p name value_sort round_ops; + ignore @@ check_test p (Irj_file.Filename name) value_sort round_ops; Config.debug_flag := true; Cli.result_print "%s" name; None diff --git a/src/mlang/test_framework/test_interpreter.mli b/src/mlang/test_framework/test_interpreter.mli index 140599288..5b8f37e85 100644 --- a/src/mlang/test_framework/test_interpreter.mli +++ b/src/mlang/test_framework/test_interpreter.mli @@ -27,7 +27,7 @@ val check_all_tests : val check_test : Mir.program -> - string -> + Irj_file.input -> Config.value_sort -> Config.round_ops -> target_dbg_info list @@ -35,3 +35,5 @@ val check_test : val check_one_test : Mir.program -> string -> Config.value_sort -> Config.round_ops -> unit (** Same as [check_all_tests], but for one test. *) + +exception InterpError of int From b30ccf354d44f431943c0ecab619a73903f569f9 Mon Sep 17 00:00:00 2001 From: Alexandre Doussot Date: Tue, 2 Dec 2025 16:10:28 +0100 Subject: [PATCH 12/14] Add server --- src/dune | 8 +++ src/server.ml | 122 ++++++++++++++++++++++++++++++++++++++++++++ src/serverDriver.ml | 70 +++++++++++++++++++++++++ 3 files changed, 200 insertions(+) create mode 100644 src/server.ml create mode 100644 src/serverDriver.ml diff --git a/src/dune b/src/dune index 07489c622..4a30ea5bf 100644 --- a/src/dune +++ b/src/dune @@ -24,3 +24,11 @@ (:standard (:include linking-flags-mlang.sexp))) (libraries mlang)) + +(executable + (name server) + (package mlang) + (public_name server) + (preprocess + (pps lwt_ppx ppx_yojson_conv)) + (libraries mlang dream)) diff --git a/src/server.ml b/src/server.ml new file mode 100644 index 000000000..47fe1be5d --- /dev/null +++ b/src/server.ml @@ -0,0 +1,122 @@ +open Ppx_yojson_conv_lib.Yojson_conv.Primitives +module Errors = Mlang.Errors +module Json = Yojson.Safe.Util + +let asf = Format.asprintf + +type file_assoc = string * string [@@deriving yojson] + +type filemap = file_assoc list [@@deriving yojson] + +module Msg = struct + type 'a msg_result = Ok of 'a | Err of string + + let yojson_of_msg_result aconv t = + match t with + | Err s -> `Assoc [ ("ok", `Bool false); ("error", `String s) ] + | Ok s -> `Assoc [ ("ok", `Bool true); ("value", aconv s) ] + + let msg_result_of_yojson _ _ = assert false + + module Out = struct + type parsing = { id : string; payload : string } [@@deriving yojson] + + let parsing name = + let m = { id = "parsing"; payload = name } in + let json = yojson_of_parsing m in + Yojson.Safe.to_string json + + type end_parsing = { id : string; payload : string } [@@deriving yojson] + + let end_parsing name = + let m = { id = "parsing-end"; payload = name } in + yojson_of_end_parsing m |> Yojson.Safe.to_string + + type 'a run_ret = { id : string; payload : 'a msg_result } + [@@deriving yojson] + + let run_ret (payload : 'a msg_result) = + let m = { id = "run-ret"; payload } in + yojson_of_run_ret yojson_of_string m |> Yojson.Safe.to_string + end + + module In = struct + type run = { + filemap : filemap; + application : string; + target : string; + irj_contents : string; + } + [@@deriving yojson] + end +end + +let make_callbacks socket : ServerDriver.callbacks = + let start_parsing name = + let msg = Msg.Out.parsing name in + Dream.send socket msg + in + let end_parsing name = + let msg = Msg.Out.end_parsing name in + Dream.send socket msg + in + { start_parsing; end_parsing } + +let parse_files socket payload = + let filemap = filemap_of_yojson payload in + let callbacks = make_callbacks socket in + ServerDriver.parse_files callbacks filemap + +let run socket payload = + let payload : Msg.In.run = Msg.In.run_of_yojson payload in + let callbacks = make_callbacks socket in + try + let%lwt dbg_info = + ServerDriver.run callbacks payload.filemap payload.irj_contents + payload.target payload.application + in + Format.printf "%s@." dbg_info; + let msg = Msg.Out.run_ret @@ Ok dbg_info in + Dream.send socket msg + with + | Errors.StructuredError (a, b, _) -> + let msg = asf "%a@." Errors.format_structured_error (a, b) in + let msg = Msg.Out.run_ret @@ Err msg in + Dream.send socket msg + | e -> + let msg = Printexc.to_string e in + let msg = Msg.Out.run_ret @@ Err msg in + let%lwt () = Dream.send socket msg in + raise e + +let () = + Dream.run ~port:4242 + @@ Dream.router + [ + Dream.get "/websocket" (fun _ -> + Dream.websocket (fun socket -> + let rec loop () = + match%lwt Dream.receive socket with + | Some msg -> + print_endline msg; + let json = Yojson.Safe.from_string msg in + let id = Json.member "id" json in + let payload = Json.member "payload" json in + let%lwt () = + match id with + | `Null -> Lwt.return () + | `String "parse-files" -> + Lwt.return @@ ignore @@ parse_files socket payload + | `String "run" -> + Lwt.return @@ ignore @@ run socket payload + | `String id -> + Dream.send socket + (Format.asprintf + {|{"msg": "unkown message '%s'"}|} id) + | _ -> assert false + in + loop () + | _ -> Dream.close_websocket socket + in + loop ())); + ] diff --git a/src/serverDriver.ml b/src/serverDriver.ml new file mode 100644 index 000000000..929b1136c --- /dev/null +++ b/src/serverDriver.ml @@ -0,0 +1,70 @@ +open Mlang + +type file_assoc = string * string + +type filemap = file_assoc list + +type callbacks = { + start_parsing : string -> unit Lwt.t; + end_parsing : string -> unit Lwt.t; +} + +let parse_file callbacks (name, contents) = + let%lwt () = callbacks.start_parsing name in + print_string "parsing "; + print_endline name; + try + let filebuf = Lexing.from_string contents in + print_endline "after lexing."; + let parsed = Mlang.Parsing.parse_lexbuf filebuf name in + let%lwt () = callbacks.end_parsing name in + Lwt.return parsed + with Errors.StructuredError (msg, pos_list, _kont) as _e -> + Cli.error_print "%a" Errors.format_structured_error (msg, pos_list); + Lwt.return [] + +let parse_files callbacks flat_filemap = + let t = Lwt_list.map_s (parse_file callbacks) flat_filemap in + t + +let run callbacks flat_filemap irj_contents target application = + Config.mpp_function := target; + Config.application_names := [ application ]; + Config.plain_output := true; + let filemap = + List.fold_left + (fun map (name, contents) -> StrMap.add name contents map) + StrMap.empty flat_filemap + in + Config.platform := Server filemap; + let dgfip_m = Mlang.Parsing.parse_m_dgfip (fun _ -> ()) [] in + let%lwt m_program = parse_files callbacks flat_filemap in + let m_program = + dgfip_m @ m_program + |> Mlang.Parsing.patch_rule_1 !Config.backend !Config.dgfip_flags + in + let m_program = Mlang.Expander.proceed m_program in + print_endline "proceeding"; + let m_program = Mlang.Validator.proceed !Config.mpp_function m_program in + print_endline "translating"; + let m_program = Mlang.Mast_to_mir.translate m_program in + print_endline "expanding functions"; + let m_program = Mir.expand_functions m_program in + print_endline "before runnning"; + let dbg_infos = + Mlang.Test_interpreter.check_test m_program (Contents irj_contents) + !Config.value_sort !Config.round_ops + in + let buf = Buffer.create 10000 in + let fmt = Format.formatter_of_buffer buf in + let delim = ref "" in + Buffer.add_char buf '['; + dbg_infos + |> List.iter (fun Test_interpreter.{ target; dbg_info } -> + Buffer.add_string buf !delim; + delim := ","; + Format.fprintf fmt {|{"target": "%s", "dbg_info": |} target; + Dbg_info.to_json fmt dbg_info; + Buffer.add_string buf "}"); + Buffer.add_char buf ']'; + Lwt.return @@ (Buffer.to_bytes buf |> Bytes.to_string) From e28c028491104f53e41b62fde7158328ed9e5f8e Mon Sep 17 00:00:00 2001 From: Alexandre Doussot Date: Mon, 1 Dec 2025 11:46:25 +0100 Subject: [PATCH 13/14] update m tests --- tests/mlang/calcul.irj | 14 +++++++++++++ tests/mlang/calcul.m | 21 +++++++++++++++++++ tests/mlang/demo.m | 21 +++++++++++++++++++ tests/mlang/erreur.irj | 16 +++++++++++++++ tests/mlang/erreur.m | 21 +++++++++++++++++++ tests/mlang/graph_deps.irj | 14 +++++++++++++ tests/mlang/graph_deps.m | 36 ++++++++++++++++++++++++++++++++ tests/mlang/m_ext.irj | 10 +++++++++ tests/mlang/m_ext.m | 42 ++++++++++++++++++++++++++++++++++++++ tests/mlang/tab.irj | 12 +++++++++++ tests/mlang/tab.m | 20 ++++++++++++++++++ 11 files changed, 227 insertions(+) create mode 100644 tests/mlang/calcul.irj create mode 100644 tests/mlang/calcul.m create mode 100644 tests/mlang/demo.m create mode 100644 tests/mlang/erreur.irj create mode 100644 tests/mlang/erreur.m create mode 100644 tests/mlang/graph_deps.irj create mode 100644 tests/mlang/graph_deps.m create mode 100644 tests/mlang/m_ext.irj create mode 100644 tests/mlang/m_ext.m create mode 100644 tests/mlang/tab.irj create mode 100644 tests/mlang/tab.m diff --git a/tests/mlang/calcul.irj b/tests/mlang/calcul.irj new file mode 100644 index 000000000..780085aca --- /dev/null +++ b/tests/mlang/calcul.irj @@ -0,0 +1,14 @@ +#NOM +TOTO +#ENTREES-PRIMITIF +V_ANCSDED/2022 +X/0 +ENTREE/24000 +#CONTROLES-PRIMITIF +#RESULTATS-PRIMITIF +X/3 +#ENTREES-RAPPELS +#CONTROLES-RAPPELS +#RESULTATS-RAPPELS +## + diff --git a/tests/mlang/calcul.m b/tests/mlang/calcul.m new file mode 100644 index 000000000..4d1ccffea --- /dev/null +++ b/tests/mlang/calcul.m @@ -0,0 +1,21 @@ +application app; + +V_ANCSDED : saisie revenu acompte = 0 avfisc = 0 categorie_TL = 0 classe = 0 cotsoc = 0 ind_abat = 0 modcat = 0 nat_code = 0 primrest = 0 priorite = 0 rapcat = 0 sanction = 0 alias V_POUET: "v_ancsed"; +X: calculee restituee primrest = 0: "x"; +Y: calculee primrest = 0: "y"; +TXMARJ: calculee primrest = 0: "TXMARJ"; +ANNEE: calculee primrest = 0: "annee en cours"; +TAUX: const=20; +REVENU : calculee restituee primrest = 0: "revenu en fin"; +ENTREE: saisie revenu acompte = 0 avfisc = 0 categorie_TL = 0 classe = 0 cotsoc = 0 ind_abat = 0 modcat = 0 nat_code = 0 primrest = 0 priorite = 0 rapcat = 0 sanction = 0 alias IDEF: "entree suite IRDV"; + +regle 1337: +application: app; + +X = 3; +Y = 3 + X * TAUX; +TXMARJ = Y - TAUX; +REVENU = ENTREE * TAUX / 100 + TXMARJ; +cible target: +application: app; +calculer domaine primitive; diff --git a/tests/mlang/demo.m b/tests/mlang/demo.m new file mode 100644 index 000000000..4d1ccffea --- /dev/null +++ b/tests/mlang/demo.m @@ -0,0 +1,21 @@ +application app; + +V_ANCSDED : saisie revenu acompte = 0 avfisc = 0 categorie_TL = 0 classe = 0 cotsoc = 0 ind_abat = 0 modcat = 0 nat_code = 0 primrest = 0 priorite = 0 rapcat = 0 sanction = 0 alias V_POUET: "v_ancsed"; +X: calculee restituee primrest = 0: "x"; +Y: calculee primrest = 0: "y"; +TXMARJ: calculee primrest = 0: "TXMARJ"; +ANNEE: calculee primrest = 0: "annee en cours"; +TAUX: const=20; +REVENU : calculee restituee primrest = 0: "revenu en fin"; +ENTREE: saisie revenu acompte = 0 avfisc = 0 categorie_TL = 0 classe = 0 cotsoc = 0 ind_abat = 0 modcat = 0 nat_code = 0 primrest = 0 priorite = 0 rapcat = 0 sanction = 0 alias IDEF: "entree suite IRDV"; + +regle 1337: +application: app; + +X = 3; +Y = 3 + X * TAUX; +TXMARJ = Y - TAUX; +REVENU = ENTREE * TAUX / 100 + TXMARJ; +cible target: +application: app; +calculer domaine primitive; diff --git a/tests/mlang/erreur.irj b/tests/mlang/erreur.irj new file mode 100644 index 000000000..3570934cc --- /dev/null +++ b/tests/mlang/erreur.irj @@ -0,0 +1,16 @@ +#NOM +TOTO +#ENTREES-PRIMITIF +V_ANCSDED/2022 +X/0 + +#CONTROLES-PRIMITIF +A100 +#RESULTATS-PRIMITIF +X/3 +#ENTREES-RAPPELS +#CONTROLES-RAPPELS +A100 +#RESULTATS-RAPPELS +## + diff --git a/tests/mlang/erreur.m b/tests/mlang/erreur.m new file mode 100644 index 000000000..3e05ed2f3 --- /dev/null +++ b/tests/mlang/erreur.m @@ -0,0 +1,21 @@ +A100:anomalie :"famille":"code_bo":"sous_code":"libelle":"is_isf"; +application app; + +V_ANCSDED : saisie revenu acompte = 0 avfisc = 0 categorie_TL = 0 classe = 0 cotsoc = 0 ind_abat = 0 modcat = 0 nat_code = 0 primrest = 0 priorite = 0 rapcat = 0 sanction = 0 alias V_ANCSDED : "v_ancsed"; +X: calculee restituee primrest = 0: "x"; + +regle 1: +application: app; +X = 3; + +verif 1: +application: app; + +si X > 1 alors erreur A100; + +cible target: +application: app; +calculer domaine primitive; +afficher_erreur "whatever\n"; +leve_erreur A100; +finalise_erreurs; diff --git a/tests/mlang/graph_deps.irj b/tests/mlang/graph_deps.irj new file mode 100644 index 000000000..82a7d0c28 --- /dev/null +++ b/tests/mlang/graph_deps.irj @@ -0,0 +1,14 @@ +#NOM +TOTO +#ENTREES-PRIMITIF +V_ANCSDED/2022 +X/0 +INPUT_DEFINED/42 +#CONTROLES-PRIMITIF +#RESULTATS-PRIMITIF +X/3 +#ENTREES-RAPPELS +#CONTROLES-RAPPELS +#RESULTATS-RAPPELS +## + diff --git a/tests/mlang/graph_deps.m b/tests/mlang/graph_deps.m new file mode 100644 index 000000000..9f2b80f14 --- /dev/null +++ b/tests/mlang/graph_deps.m @@ -0,0 +1,36 @@ +application app; + +V_ANCSDED : saisie revenu acompte = 0 avfisc = 0 categorie_TL = 0 classe = 0 cotsoc = 0 ind_abat = 0 modcat = 0 nat_code = 0 primrest = 0 priorite = 0 rapcat = 0 sanction = 0 alias V_POUET: "v_ancsed"; +X: calculee restituee primrest = 0: "x"; +Y: calculee primrest = 0: "y"; +MULTILINE: calculee primrest = 0 : "multiline"; +TXMARJ: calculee primrest = 0: "tx_marj"; +ANNEE: calculee primrest = 0: "annee"; +Z: calculee primrest = 0: "z"; +VARTMP: calculee primrest = 0: "vartmp"; +A: calculee primrest = 0: "a"; +CONST: const=6; +FLOAT: calculee primrest = 0: "float"; +BLABLA: saisie revenu acompte = 0 avfisc = 0 categorie_TL = 0 classe = 0 cotsoc = 0 ind_abat = 0 modcat = 0 nat_code = 0 primrest = 0 priorite = 0 rapcat = 0 sanction = 0 alias V_BLA: "blabla"; +INPUT_UNDEFINED: saisie revenu acompte = 0 avfisc = 0 categorie_TL = 0 classe = 0 cotsoc = 0 ind_abat = 0 modcat = 0 nat_code = 0 primrest = 0 priorite = 0 rapcat = 0 sanction = 0 alias IUND: "blabla"; +INPUT_DEFINED: saisie revenu acompte = 0 avfisc = 0 categorie_TL = 0 classe = 0 cotsoc = 0 ind_abat = 0 modcat = 0 nat_code = 0 primrest = 0 priorite = 0 rapcat = 0 sanction = 0 alias IDEF: "blabla"; +TAB: tableau[10] calculee primrest = 0 base : "tableau"; + +regle 1337: +application: app; + +VARTMP = 0; +TAB[0] = TAB[0] + VARTMP; +Z = INPUT_UNDEFINED; +FLOAT = 0.1230; +X = 0 + 1 + 2 + FLOAT - FLOAT + VARTMP; +Y = 3 + X * CONST; +TXMARJ = Y - CONST; +ANNEE = TXMARJ + INPUT_UNDEFINED + INPUT_DEFINED; +MULTILINE = X ++ Y; +VARTMP = 1; +A = VARTMP; +cible target: +application: app; +calculer domaine primitive; diff --git a/tests/mlang/m_ext.irj b/tests/mlang/m_ext.irj new file mode 100644 index 000000000..46ad09223 --- /dev/null +++ b/tests/mlang/m_ext.irj @@ -0,0 +1,10 @@ +#NOM +TOTO +#ENTREES-PRIMITIF +#CONTROLES-PRIMITIF +#RESULTATS-PRIMITIF +#ENTREES-RAPPELS +#CONTROLES-RAPPELS +#RESULTATS-RAPPELS +## + diff --git a/tests/mlang/m_ext.m b/tests/mlang/m_ext.m new file mode 100644 index 000000000..c2088a455 --- /dev/null +++ b/tests/mlang/m_ext.m @@ -0,0 +1,42 @@ +application test; + +V_ANCSDED : saisie revenu acompte = 0 avfisc = 0 categorie_TL = 0 classe = 0 cotsoc = 0 ind_abat = 0 modcat = 0 nat_code = 0 primrest = 0 priorite = 0 rapcat = 0 sanction = 0 alias V_POUET: "v_ancsed"; + + +fonction toto_fonction: +application: test; +arguments: A0, A1, A2, A3, A4, A5, A6; +resultat: R; +variables_temporaires: PROUT0, PROUT1, PROUT2; +R = A0 + A1 + A2 + A3 + A4 + A5 + A6; + +cible toto_cible: +application: test; +arguments: A0, A1, A2, A3, A4, A5, A6, R; +iterer : variable PROUT0 : categorie calculee * : dans ( + iterer : variable PROUT1 : categorie calculee * : dans ( + iterer : variable PROUT2 : categorie calculee * : dans ( + R = A0 + A1 + A2 + A3 + A4 + A5 + A6; + ) + ) +) + +cible test_args: +application: test; +variables_temporaires: A0, A1, A2, A3, AA tableau[3], A4, A5, A6, R; +afficher_erreur "entree test_args\n" indenter(2); +iterer : variable I : entre 0..6 increment 1 : dans ( + A0 = 0; + A1 = 1; + A2 = 2; + A3 = 3; + A4 = 4; + A5 = 5; + A6 = 6; +) +R = 7; +calculer cible toto_cible : avec A0, A1, A2, A3, A4, A5, A6, R; +afficher_erreur "toto_cible(...) = " (R) "\n"; +afficher_erreur "toto_fonction(...) = "; +afficher_erreur "\n"; +afficher_erreur indenter(-2) "sortie test_args\n"; diff --git a/tests/mlang/tab.irj b/tests/mlang/tab.irj new file mode 100644 index 000000000..3fb9ea955 --- /dev/null +++ b/tests/mlang/tab.irj @@ -0,0 +1,12 @@ +#NOM +TOTO +#ENTREES-PRIMITIF +V_ANCSDED/2022 +Y/12 +#CONTROLES-PRIMITIF +#RESULTATS-PRIMITIF +#ENTREES-RAPPELS +#CONTROLES-RAPPELS +#RESULTATS-RAPPELS +## + diff --git a/tests/mlang/tab.m b/tests/mlang/tab.m new file mode 100644 index 000000000..8029cc3a4 --- /dev/null +++ b/tests/mlang/tab.m @@ -0,0 +1,20 @@ +application app; + +V_ANCSDED : saisie revenu acompte = 0 avfisc = 0 categorie_TL = 0 classe = 0 cotsoc = 0 ind_abat = 0 modcat = 0 nat_code = 0 primrest = 0 priorite = 0 rapcat = 0 sanction = 0 alias V_POUET: "v_ancsed"; +X : calculee restituee primrest = 0 base : "x"; +Y : calculee restituee primrest = 0 base : "y"; +Z : calculee restituee primrest = 0 base : "z"; +TAB: tableau[10] calculee primrest = 0 base : "tableau"; + +regle 1337: +application: app; +TAB[1] = 3; +TAB[0] = Y; +TAB[2] = TAB[1]; +Z = TAB[3]; +X = TAB[0]; +Z = 123; + +cible target: +application: app; +calculer domaine primitive; From ef8a773a711529ec170f4419f9edaf7acf6944b3 Mon Sep 17 00:00:00 2001 From: Alexandre Doussot Date: Tue, 2 Dec 2025 17:08:28 +0100 Subject: [PATCH 14/14] properly print floats --- src/mlang/m_ir/com.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mlang/m_ir/com.ml b/src/mlang/m_ir/com.ml index 81f2b7e14..d82ffe98a 100644 --- a/src/mlang/m_ir/com.ml +++ b/src/mlang/m_ir/com.ml @@ -1132,8 +1132,9 @@ let format_value_typ fmt t = | Real -> "REEL") let format_literal fmt l = - Format.pp_print_string fmt - (match l with Float f -> string_of_float f | Undefined -> "indefini") + match l with + | Float f -> Format.fprintf fmt "%g" f + | Undefined -> Format.pp_print_string fmt "indefini" let format_atom form_var fmt vl = match vl with