From 19617546dd81ee8c7bcf844c0ac4b5c08f74c877 Mon Sep 17 00:00:00 2001 From: Mateo Date: Mon, 5 Aug 2024 12:30:21 +0200 Subject: [PATCH 1/7] refactor: remove unneeded length on special insert --- src/lsp/cobol_data/data_picture.ml | 16 ++++------------ src/lsp/cobol_data/data_picture.mli | 8 +------- test/cobol_parsing/test_picture_parsing.ml | 12 ++++-------- 3 files changed, 9 insertions(+), 27 deletions(-) diff --git a/src/lsp/cobol_data/data_picture.ml b/src/lsp/cobol_data/data_picture.ml index cc08118ad..cf4f851a8 100644 --- a/src/lsp/cobol_data/data_picture.ml +++ b/src/lsp/cobol_data/data_picture.ml @@ -107,7 +107,7 @@ module TYPES = struct and basic_edition = | SimpleInsertion of simple_insertion - | SpecialInsertion of special_insertion + | SpecialInsertion of int | FixedInsertion of fixed_insertion (* The following may later becore mappings from Int to symbols, and even @@ -119,12 +119,6 @@ module TYPES = struct simple_insertion_offset: int; } - and special_insertion = - { - special_insertion_offset: int; - special_insertion_length: int; - } - and fixed_insertion = { fixed_insertion_symbol: symbol; @@ -325,14 +319,13 @@ let data_size: category -> int = function let edited_size: category -> int = let simple_insertion_size { simple_insertion_symbols = symbols; _ } = - symbols.symbol_occurences - and special_insertion_size { special_insertion_length = n; _ } = n in + symbols.symbol_occurences in let simple_insertions_size = List.fold_left (fun s i -> s + simple_insertion_size i) 0 and basic_editions_size basics = List.fold_left begin fun s -> function | SimpleInsertion i -> s + simple_insertion_size i - | SpecialInsertion i -> s + special_insertion_size i + | SpecialInsertion _ | FixedInsertion _ -> s + 1 end 0 basics in @@ -563,8 +556,7 @@ let append category ~after_v ({ symbol; symbol_occurences = n } as symbols) = | _ -> error and append_special_insertion offset = function | FixedNum { digits; scale; with_sign; editions } -> - let special = SpecialInsertion { special_insertion_offset = offset; - special_insertion_length = n } in + let special = SpecialInsertion offset in Ok (numeric ~with_sign digits scale ~editions:{ editions with basics = special :: editions.basics }) | _ -> error diff --git a/src/lsp/cobol_data/data_picture.mli b/src/lsp/cobol_data/data_picture.mli index cb4e81cbe..c7c4ff8ea 100644 --- a/src/lsp/cobol_data/data_picture.mli +++ b/src/lsp/cobol_data/data_picture.mli @@ -90,7 +90,7 @@ module TYPES: sig and basic_edition = | SimpleInsertion of simple_insertion - | SpecialInsertion of special_insertion + | SpecialInsertion of int | FixedInsertion of fixed_insertion and simple_insertion = @@ -99,12 +99,6 @@ module TYPES: sig simple_insertion_offset: int; } - and special_insertion = - { - special_insertion_offset: int; - special_insertion_length: int; - } - and fixed_insertion = { fixed_insertion_symbol: symbol; diff --git a/test/cobol_parsing/test_picture_parsing.ml b/test/cobol_parsing/test_picture_parsing.ml index cf76a07c0..6a8bf3d12 100644 --- a/test/cobol_parsing/test_picture_parsing.ml +++ b/test/cobol_parsing/test_picture_parsing.ml @@ -328,8 +328,7 @@ module Pictures = struct simple_insertion_offset = 0 }; SimpleInsertion { simple_insertion_symbols = comma 1; simple_insertion_offset = 5 }; - SpecialInsertion { special_insertion_offset = 9; - special_insertion_length = 1 } ] + SpecialInsertion 9 ] in { category = fixednum 9 3 ~basics; pic = [comma 2; nine 3; comma 1; nine 3; dot 1; nine 3] } @@ -361,8 +360,7 @@ module Pictures = struct and basics = [ SimpleInsertion { simple_insertion_symbols = comma 1; simple_insertion_offset = 3 }; - SpecialInsertion { special_insertion_offset = 7; - special_insertion_length = 1 } ] + SpecialInsertion 7 ] in { category = fixednum 9 3 ~basics ~zerorepl; pic = [z 3; comma 1; z 3; dot 1; z 3] } @@ -413,8 +411,7 @@ module Pictures = struct let basics = [ SimpleInsertion { simple_insertion_symbols = comma 1; simple_insertion_offset = 3 }; - SpecialInsertion { special_insertion_offset = 7; - special_insertion_length = 1 } ] + SpecialInsertion 7 ] in { category = floatnum 9 3 3 ~basics; pic = [nine 3; comma 1; nine 3; dot 1; nine 3; e 1; plus 1; nine 3] } @@ -446,8 +443,7 @@ module Pictures = struct let pic_ppvpp = let basics = - [ SpecialInsertion { special_insertion_offset = 2; - special_insertion_length = 1 } ] + [ SpecialInsertion 2 ] and floating = { floating_insertion_symbol = Plus; floating_insertion_ranges = [{ floating_range_offset = 0; From 3cc20234c7658addfb7129640c665aabedc89a49 Mon Sep 17 00:00:00 2001 From: Mateo Date: Mon, 5 Aug 2024 13:55:19 +0200 Subject: [PATCH 2/7] feat: example value display on numeric-edited hover --- src/lsp/cobol_data/data_picture.ml | 242 +++++++++++++++++++++ src/lsp/cobol_data/data_picture.mli | 3 + src/lsp/cobol_lsp/lsp_data_info_printer.ml | 37 +++- test/lsp/lsp_hover.ml | 61 +++++- 4 files changed, 329 insertions(+), 14 deletions(-) diff --git a/src/lsp/cobol_data/data_picture.ml b/src/lsp/cobol_data/data_picture.ml index cf4f851a8..1b575a804 100644 --- a/src/lsp/cobol_data/data_picture.ml +++ b/src/lsp/cobol_data/data_picture.ml @@ -1281,3 +1281,245 @@ let unit_test ?(config=config) ~expect picture = end else true + +let simple_insertion_char_of ~symbol = + match symbol with + | B -> ' ' + | Zero -> '0' + | Slant -> '/' + | DecimalSep -> '.' + | GroupingSep -> ',' + | _ -> Pretty.invalid_arg + "Not a simple insertion symbol '%a'" + pp_symbol_cobolized symbol + +let fixed_insertion_str_of ~symbol ~is_negative = + match symbol with + | CS -> "$" + | Plus | Minus when is_negative -> "-" + | Plus -> "+" + | Minus -> " " + | CR | DB when not is_negative -> " " + | CR -> "CR" + | DB -> "DB" + | _ -> Pretty.invalid_arg + "Not a fixed insertion symbol '%a'" + pp_symbol_cobolized symbol + +let do_basic_edit_on ~is_negative basic s = + let (offset, insertion) = + match basic with + | SimpleInsertion + { simple_insertion_symbols = { symbol_occurences = n; symbol }; + simple_insertion_offset = offset } -> + offset, String.make n @@ simple_insertion_char_of ~symbol + | SpecialInsertion offset -> + offset, "." + | FixedInsertion { fixed_insertion_symbol = symbol; fixed_insertion_offset = offset } -> + offset, fixed_insertion_str_of ~symbol ~is_negative + in + Str.string_before s offset + ^ insertion + ^ Str.string_after s offset + +let all_repl_indexes_from ~ranges s digits = + let indexes = + ranges + |> List.rev_map begin fun { floating_range_offset = offset; + floating_range_length = len } -> + List.init len (fun i -> offset + i) + end + |> List.flatten |> List.sort Int.compare + in + let is_only_repl_char = List.length indexes >= digits in + let min_index = List.hd indexes in + let (_, (all_indexes, all_zero)) = String.fold_left + begin fun ((idx, (acc_repl, should_continue_repl)) as acc) ch -> + if not should_continue_repl then acc else + if List.mem idx indexes + then (idx+1, + if ch == '0' + then (idx::acc_repl, true) + else (acc_repl, false)) + else + if min_index < idx && List.mem ch [' '; ','] + then (idx+1, (idx::acc_repl, true)) + else (idx+1, (acc_repl, should_continue_repl)) + end (0, ([], true)) s + in all_indexes, all_zero && is_only_repl_char + +let do_floatedit_n_zerorepl_on digits is_negative + symbol ranges s = + if ranges == [] then s else + let floating_last_ch = match symbol with + | Plus | Minus when is_negative -> '-' + | Plus -> '+' + | Minus -> ' ' + | CS -> '$' + | Z -> ' ' + | Star -> '*' + | _ -> Pretty.invalid_arg + "Floating edit or zero replacement symbol '%a' is invalid" + pp_symbol_cobolized symbol + in + let repl_ch = match symbol with + | Minus | Plus | CS | Z -> ' ' + | Star -> '*' + | _ -> Pretty.invalid_arg + "Floating edit or zero replacement symbol '%a' is invalid" + pp_symbol_cobolized symbol + in + let repl_str = String.make 1 repl_ch in + let all_repl_indexes, repl_everything = + all_repl_indexes_from ~ranges s digits in + if repl_everything + then + String.map begin fun ch -> + if ch == '.' && symbol == Star + then '.' + else repl_ch + end s + else + let (_, _, last_repl_idx, res) = String.fold_left + begin fun (i, after_decimal_point, last_repl_idx, res) ch -> + let orig_str = String.make 1 ch in + if after_decimal_point + then (i+1, after_decimal_point, last_repl_idx, res ^ orig_str) + else + if ch == '.' + then (i+1, true, last_repl_idx, res ^ ".") + else + if List.mem i all_repl_indexes + then (i+1, after_decimal_point, i, res ^ repl_str) + else (i+1, after_decimal_point, last_repl_idx, res ^ orig_str) + end (0, false, -1, "") s + in + String.mapi begin fun i ch -> + if i == last_repl_idx + then floating_last_ch + else ch + end + res + +let rec edit_basics ~is_negative basics s = + match basics with + | [] -> s + | hd::tl -> + do_basic_edit_on ~is_negative hd s + |> edit_basics ~is_negative tl + +let simple_example_of ~digits ~scale ~with_dot value = + let str_val = string_of_float (Float.abs value) in + let i = String.index str_val '.' in + let whole_part = Str.string_before str_val i in + let whole_len = String.length whole_part in + let floating_part = Str.string_after str_val (i+1) in + let required_len = digits - scale in + (String.init required_len + (fun i -> + if i < required_len - whole_len + then '0' + else whole_part.[i - (required_len - whole_len)]) + ) + ^ (if scale > 0 + then + (if with_dot then "." else "") + ^ String.init scale + (fun i -> + if i < String.length floating_part + then floating_part.[i] + else '0') + else "") + +let example_of ~picture value = + if List.exists (fun { symbol; _ } -> symbol == P) picture.pic + then "No example with P yet" (* /!\ scale can be negative: PIC 9P *) + else + match picture.category with + | Alphabetic _ | Boolean _ | National _ | Alphanumeric _ -> "" + | FloatNum _ -> "floatnum todo" + | FixedNum { digits; scale; with_sign; _ } + when not @@ is_edited picture -> + (if with_sign then "+" else "") + ^ simple_example_of ~digits ~scale ~with_dot:true value + | FixedNum { digits; scale; with_sign; + editions = { basics; floating; zerorepl } } -> + ignore (with_sign); + let is_negative = value < 0. in + let edit_zerorepl = Option.fold ~none:Fun.id + ~some:(fun { zero_replacement_symbol = symbol; + zero_replacement_ranges = ranges } -> + do_floatedit_n_zerorepl_on digits is_negative symbol ranges) + zerorepl in + let edit_floating = Option.fold ~none:Fun.id + ~some:(fun { floating_insertion_symbol = symbol; + floating_insertion_ranges = ranges } -> + do_floatedit_n_zerorepl_on digits is_negative symbol ranges) + floating in + try + (if Option.is_some floating + then "0" + else "") + ^ simple_example_of ~digits ~scale ~with_dot:false value + |> edit_basics ~is_negative:(value < 0.) basics + |> edit_zerorepl + |> edit_floating + with Invalid_argument e -> + Pretty.invalid_arg + "Unable to build example of picture, error '%s'" e + + + +let unit_tests = + [ ("99,B999,B000", 1234., "01, 234, 000"); + ("99,999", 12345., "12,345"); + ("999.99", 1.234, "001.23"); + ("999.99", 12.34, "012.34"); + ("999.99", 123.45, "123.45"); + ("999.99", 1234.5, "234.50"); + (* ("+999.99E+99", 12345., "+123.45E+02"); *) + ("999.99+", +6555.556, "555.55+"); + ("+9999.99", -6555.555, "-6555.55"); + ("9999.99", +1234.56, "1234.56"); + ("$999.99", -123.45, "$123.45"); + ("-$999.99", -123.456, "-$123.45"); + ("-$999.99", +123.456, " $123.45"); + ("$9999.99CR", +123.45, "$0123.45 "); + ("$9999.99DB", -123.45, "$0123.45DB"); + (* ("U999.99", -123.45, "EUR123.45"); *) + (* ("-u999.99", -123.456, "-USD123.45"); *) + ("$$$$.99", 0.123, " $.12"); + ("$$$9.99", 0.12, " $0.12"); + ("$,$$$,999.99", -1234.56, " $1,234.56"); + (* ("U,UUU,UU9.99-", -1234.56, "EUR1,234.56-"); *) + (* ("u,uuu,uu9.99", 1234.56, "USD1,234.56"); *) + ("+,+++,999.99", -123456.789, " -123,456.78"); + ("$$,$$$,$$$.99CR", -1234567., "$1,234,567.00CR"); + ("++,+++,+++.+++", 0000.00, " "); + ("$B++/+++.+", 0000.00, " "); + ("****.**", 0000.00, "****.**"); + ("ZZZZ.ZZ", 0000.00, " "); + ("ZZZZ.99", 0000.00, " .00"); + ("****.99", 0000.00, "****.00"); + ("ZZ99.99", 0000.00, " 00.00"); + ("Z,ZZZ.ZZ+", +123.456, " 123.45+"); + ("*,***.**+", -123.45, "**123.45-"); + ("**,***,***.**", +12345678.9, "12,345,678.90"); + (* ("$Z,ZZZ,ZZZ.ZZCR", +12345.67, "$ 12,345.67"); *) + ("$B*,***.**BBDB", 0., "*******.******"); + ("$B*,***,***.**BBDB", -12345.67, "$ ***12,345.67 DB");] + +let () = + let rec test = function + | [] -> () + | (pic, value, expected)::tl -> + match of_string config pic with + | Ok picture -> + let example = example_of ~picture value in + let error_msg = Pretty.to_string "ERROR: different result (%s, %f) -> '%s' (expected: '%s')" pic value example expected in + if String.equal example expected + then test tl + else failwith error_msg + | Error _ -> + failwith @@ Pretty.to_string "ERROR: Unable to form picture with picture-string '%s'\n" pic + in test unit_tests diff --git a/src/lsp/cobol_data/data_picture.mli b/src/lsp/cobol_data/data_picture.mli index c7c4ff8ea..3d19f29bf 100644 --- a/src/lsp/cobol_data/data_picture.mli +++ b/src/lsp/cobol_data/data_picture.mli @@ -237,3 +237,6 @@ val pp_meaning_of_precedence_index stderr and returns `false` *) val unit_test : ?config:TYPES.config -> expect:string -> string -> bool + +val example_of + : picture:t -> float -> string diff --git a/src/lsp/cobol_lsp/lsp_data_info_printer.ml b/src/lsp/cobol_lsp/lsp_data_info_printer.ml index c2e02a435..47a237ffe 100644 --- a/src/lsp/cobol_lsp/lsp_data_info_printer.ml +++ b/src/lsp/cobol_lsp/lsp_data_info_printer.ml @@ -25,16 +25,37 @@ let pp_cobol_block: _ Fmt.t -> _ Fmt.t = fun pp -> (* usage *) +let max_value digits scale = + let s = "123456789123456789123456789123456789" in + let whole = (digits - scale) in + let scale = if scale < 0 then 0 else scale in + let whole_part = Str.string_before s whole in + let decimal_part = Str.string_before (Str.string_after s whole) scale in + float_of_string (whole_part ^ "." ^ decimal_part) + +let pp_example_of ppf (picture: Cobol_data.Picture.t) = + match picture.category with + | FixedNum { digits; scale; _ } -> + let max = max_value digits scale in + let max_str = + if Float.is_integer max + then string_of_int (int_of_float max) + else string_of_float max in + Fmt.pf ppf "\n\nExample display value:\n- 0: `%s`\n- %s: `%s`" + (Cobol_data.Picture.example_of ~picture 0.) + max_str + (Cobol_data.Picture.example_of ~picture max) + | _ -> () + let pp_usage: usage Pretty.printer = let pp_usage_with_picture ppf name (picture: Cobol_data.Picture.t) = - Fmt.( - pp_cobol_block (fun ppf _ -> - pf ppf "PIC %a USAGE %s" - Cobol_data.Picture.pp_picture_symbols picture.pic - name) - ++ const string "\n\n" - ++ const Cobol_data.Picture.pp_category picture.category) - ppf () + Fmt.pf ppf "%a\n\n%a%a" + (pp_cobol_block (fun ppf _ -> + Fmt.pf ppf "PIC %a USAGE %s" + Cobol_data.Picture.pp_picture_symbols picture.pic + name)) () + Cobol_data.Picture.pp_category picture.category + pp_example_of picture and pp_usage_with_sign ppf name signed = pp_cobol_block Fmt.(any "USAGE " ++ any name ++ any (if signed then " SIGNED" else " UNSIGNED")) ppf () diff --git a/test/lsp/lsp_hover.ml b/test/lsp/lsp_hover.ml index 9b43a7fdd..1651d1072 100644 --- a/test/lsp/lsp_hover.ml +++ b/test/lsp/lsp_hover.ml @@ -292,6 +292,9 @@ let%expect_test "hover-typedef-vars" = PIC 999 USAGE DISPLAY ``` NUMERIC(digits = 3, scale = 0, with_sign = false) + Example display value: + - 0: `000` + - 123: `123` VALUE 123 (line 8, character 16): __rootdir__/prog.cob:9.13-9.21: @@ -390,6 +393,9 @@ let%expect_test "hover-typedef-vars" = PIC 999 USAGE DISPLAY ``` NUMERIC(digits = 3, scale = 0, with_sign = false) + Example display value: + - 0: `000` + - 123: `123` VALUE 123 (line 12, character 47): __rootdir__/prog.cob:13.44-13.52: @@ -440,6 +446,7 @@ let%expect_test "hover-typedef-vars-usage" = 01 _|_VAR6 PIC 111 USAGE BIT. 01 _|_VAR7 USAGE POINTER. 01 _|_VAR8 PIC 9 USAGE PACKED-DECIMAL. + 01 _|_VAR9 PIC $++/+.+B+. PROCEDURE DIVISION. STOP RUN. |cobol}; @@ -462,6 +469,9 @@ let%expect_test "hover-typedef-vars-usage" = PIC -BZZZ,ZZ9.99 USAGE DISPLAY ``` NUMERIC(digits = 8, scale = 2, with_sign = false) + Example display value: + - 0: ` 0.00` + - 123456.78: ` 123,456.78` (line 6, character 11): __rootdir__/prog.cob:7.11-7.15: 4 DATA DIVISION. @@ -478,6 +488,9 @@ let%expect_test "hover-typedef-vars-usage" = PIC 9 USAGE BINARY ``` NUMERIC(digits = 1, scale = 0, with_sign = false) + Example display value: + - 0: `0` + - 1: `1` (line 7, character 11): __rootdir__/prog.cob:8.11-8.15: 5 WORKING-STORAGE SECTION. @@ -532,7 +545,7 @@ let%expect_test "hover-typedef-vars-usage" = 11 > 01 VAR7 USAGE POINTER. ---- ^^^^ 12 01 VAR8 PIC 9 USAGE PACKED-DECIMAL. - 13 PROCEDURE DIVISION. + 13 01 VAR9 PIC $++/+.+B+. ```cobol VAR7 ``` @@ -544,16 +557,37 @@ let%expect_test "hover-typedef-vars-usage" = 11 01 VAR7 USAGE POINTER. 12 > 01 VAR8 PIC 9 USAGE PACKED-DECIMAL. ---- ^^^^ - 13 PROCEDURE DIVISION. - 14 STOP RUN. + 13 01 VAR9 PIC $++/+.+B+. + 14 PROCEDURE DIVISION. ```cobol VAR8 ``` ```cobol PIC 9 USAGE PACKED-DECIMAL ``` - NUMERIC(digits = 1, scale = 0, with_sign = false) |}];; - + NUMERIC(digits = 1, scale = 0, with_sign = false) + Example display value: + - 0: `0` + - 1: `1` + (line 12, character 11): + __rootdir__/prog.cob:13.11-13.15: + 10 01 VAR6 PIC 111 USAGE BIT. + 11 01 VAR7 USAGE POINTER. + 12 01 VAR8 PIC 9 USAGE PACKED-DECIMAL. + 13 > 01 VAR9 PIC $++/+.+B+. + ---- ^^^^ + 14 PROCEDURE DIVISION. + 15 STOP RUN. + ```cobol + VAR9 + ``` + ```cobol + PIC $++/+.+B+ USAGE DISPLAY + ``` + NUMERIC(digits = 4, scale = 2, with_sign = false) + Example display value: + - 0: ` ` + - 12.34: `$+1/2.3 4` |}];; let%expect_test "hover-typedef-filler-vars" = let { projdir; end_with_postproc }, server = make_lsp_project () in @@ -791,6 +825,9 @@ let%expect_test "hover-typedef-renames" = PIC 9 USAGE DISPLAY ``` NUMERIC(digits = 1, scale = 0, with_sign = false) + Example display value: + - 0: `0` + - 1: `1` (line 8, character 16): __rootdir__/prog.cob:9.10-9.25: 6 01 X. @@ -808,6 +845,9 @@ let%expect_test "hover-typedef-renames" = PIC 9 USAGE DISPLAY ``` NUMERIC(digits = 1, scale = 0, with_sign = false) + Example display value: + - 0: `0` + - 1: `1` (line 8, character 23): __rootdir__/prog.cob:9.23-9.24: 6 01 X. @@ -824,6 +864,9 @@ let%expect_test "hover-typedef-renames" = PIC 9 USAGE DISPLAY ``` NUMERIC(digits = 1, scale = 0, with_sign = false) + Example display value: + - 0: `0` + - 1: `1` (line 9, character 22): __rootdir__/prog.cob:10.13-10.22: 7 05 Y PIC 9. @@ -858,7 +901,10 @@ let%expect_test "hover-typedef-renames" = ```cobol PIC 9 USAGE DISPLAY ``` - NUMERIC(digits = 1, scale = 0, with_sign = false) |}];; + NUMERIC(digits = 1, scale = 0, with_sign = false) + Example display value: + - 0: `0` + - 1: `1` |}];; let%expect_test "hover-typedef-redefines" = let { projdir; end_with_postproc }, server = make_lsp_project () in @@ -913,6 +959,9 @@ let%expect_test "hover-typedef-redefines" = PIC 9 USAGE DISPLAY ``` NUMERIC(digits = 1, scale = 0, with_sign = false) + Example display value: + - 0: `0` + - 1: `1` (line 9, character 20): __rootdir__/prog.cob:10.20-10.21: 7 05 Y PIC 9. From 7f36249d43941cc13f5a4c762c3b594420e955ac Mon Sep 17 00:00:00 2001 From: Mateo Date: Mon, 5 Aug 2024 16:11:50 +0200 Subject: [PATCH 3/7] refactor: move stuff around --- src/lsp/cobol_data/data_picture.ml | 253 +------------------- src/lsp/cobol_data/data_picture.mli | 13 +- src/lsp/cobol_lsp/cobol_lsp.ml | 1 + src/lsp/cobol_lsp/lsp_data_info_printer.ml | 4 +- src/lsp/cobol_lsp/lsp_picture_interp.ml | 263 +++++++++++++++++++++ test/cobol_parsing/test_picture_parsing.ml | 8 +- test/lsp/lsp_picture_interp.ml | 74 ++++++ 7 files changed, 361 insertions(+), 255 deletions(-) create mode 100644 src/lsp/cobol_lsp/lsp_picture_interp.ml create mode 100644 test/lsp/lsp_picture_interp.ml diff --git a/src/lsp/cobol_data/data_picture.ml b/src/lsp/cobol_data/data_picture.ml index 1b575a804..b1ac5d5eb 100644 --- a/src/lsp/cobol_data/data_picture.ml +++ b/src/lsp/cobol_data/data_picture.ml @@ -107,7 +107,7 @@ module TYPES = struct and basic_edition = | SimpleInsertion of simple_insertion - | SpecialInsertion of int + | SpecialInsertion of special_insertion | FixedInsertion of fixed_insertion (* The following may later becore mappings from Int to symbols, and even @@ -119,6 +119,11 @@ module TYPES = struct simple_insertion_offset: int; } + and special_insertion = + { + special_insertion_offset: int; + } + and fixed_insertion = { fixed_insertion_symbol: symbol; @@ -554,9 +559,9 @@ let append category ~after_v ({ symbol; symbol_occurences = n } as symbols) = Ok (numeric ~with_sign ~editions digits scale) | Error () -> error) | _ -> error - and append_special_insertion offset = function + and append_special_insertion special_insertion_offset = function | FixedNum { digits; scale; with_sign; editions } -> - let special = SpecialInsertion offset in + let special = SpecialInsertion { special_insertion_offset } in Ok (numeric ~with_sign digits scale ~editions:{ editions with basics = special :: editions.basics }) | _ -> error @@ -1281,245 +1286,3 @@ let unit_test ?(config=config) ~expect picture = end else true - -let simple_insertion_char_of ~symbol = - match symbol with - | B -> ' ' - | Zero -> '0' - | Slant -> '/' - | DecimalSep -> '.' - | GroupingSep -> ',' - | _ -> Pretty.invalid_arg - "Not a simple insertion symbol '%a'" - pp_symbol_cobolized symbol - -let fixed_insertion_str_of ~symbol ~is_negative = - match symbol with - | CS -> "$" - | Plus | Minus when is_negative -> "-" - | Plus -> "+" - | Minus -> " " - | CR | DB when not is_negative -> " " - | CR -> "CR" - | DB -> "DB" - | _ -> Pretty.invalid_arg - "Not a fixed insertion symbol '%a'" - pp_symbol_cobolized symbol - -let do_basic_edit_on ~is_negative basic s = - let (offset, insertion) = - match basic with - | SimpleInsertion - { simple_insertion_symbols = { symbol_occurences = n; symbol }; - simple_insertion_offset = offset } -> - offset, String.make n @@ simple_insertion_char_of ~symbol - | SpecialInsertion offset -> - offset, "." - | FixedInsertion { fixed_insertion_symbol = symbol; fixed_insertion_offset = offset } -> - offset, fixed_insertion_str_of ~symbol ~is_negative - in - Str.string_before s offset - ^ insertion - ^ Str.string_after s offset - -let all_repl_indexes_from ~ranges s digits = - let indexes = - ranges - |> List.rev_map begin fun { floating_range_offset = offset; - floating_range_length = len } -> - List.init len (fun i -> offset + i) - end - |> List.flatten |> List.sort Int.compare - in - let is_only_repl_char = List.length indexes >= digits in - let min_index = List.hd indexes in - let (_, (all_indexes, all_zero)) = String.fold_left - begin fun ((idx, (acc_repl, should_continue_repl)) as acc) ch -> - if not should_continue_repl then acc else - if List.mem idx indexes - then (idx+1, - if ch == '0' - then (idx::acc_repl, true) - else (acc_repl, false)) - else - if min_index < idx && List.mem ch [' '; ','] - then (idx+1, (idx::acc_repl, true)) - else (idx+1, (acc_repl, should_continue_repl)) - end (0, ([], true)) s - in all_indexes, all_zero && is_only_repl_char - -let do_floatedit_n_zerorepl_on digits is_negative - symbol ranges s = - if ranges == [] then s else - let floating_last_ch = match symbol with - | Plus | Minus when is_negative -> '-' - | Plus -> '+' - | Minus -> ' ' - | CS -> '$' - | Z -> ' ' - | Star -> '*' - | _ -> Pretty.invalid_arg - "Floating edit or zero replacement symbol '%a' is invalid" - pp_symbol_cobolized symbol - in - let repl_ch = match symbol with - | Minus | Plus | CS | Z -> ' ' - | Star -> '*' - | _ -> Pretty.invalid_arg - "Floating edit or zero replacement symbol '%a' is invalid" - pp_symbol_cobolized symbol - in - let repl_str = String.make 1 repl_ch in - let all_repl_indexes, repl_everything = - all_repl_indexes_from ~ranges s digits in - if repl_everything - then - String.map begin fun ch -> - if ch == '.' && symbol == Star - then '.' - else repl_ch - end s - else - let (_, _, last_repl_idx, res) = String.fold_left - begin fun (i, after_decimal_point, last_repl_idx, res) ch -> - let orig_str = String.make 1 ch in - if after_decimal_point - then (i+1, after_decimal_point, last_repl_idx, res ^ orig_str) - else - if ch == '.' - then (i+1, true, last_repl_idx, res ^ ".") - else - if List.mem i all_repl_indexes - then (i+1, after_decimal_point, i, res ^ repl_str) - else (i+1, after_decimal_point, last_repl_idx, res ^ orig_str) - end (0, false, -1, "") s - in - String.mapi begin fun i ch -> - if i == last_repl_idx - then floating_last_ch - else ch - end - res - -let rec edit_basics ~is_negative basics s = - match basics with - | [] -> s - | hd::tl -> - do_basic_edit_on ~is_negative hd s - |> edit_basics ~is_negative tl - -let simple_example_of ~digits ~scale ~with_dot value = - let str_val = string_of_float (Float.abs value) in - let i = String.index str_val '.' in - let whole_part = Str.string_before str_val i in - let whole_len = String.length whole_part in - let floating_part = Str.string_after str_val (i+1) in - let required_len = digits - scale in - (String.init required_len - (fun i -> - if i < required_len - whole_len - then '0' - else whole_part.[i - (required_len - whole_len)]) - ) - ^ (if scale > 0 - then - (if with_dot then "." else "") - ^ String.init scale - (fun i -> - if i < String.length floating_part - then floating_part.[i] - else '0') - else "") - -let example_of ~picture value = - if List.exists (fun { symbol; _ } -> symbol == P) picture.pic - then "No example with P yet" (* /!\ scale can be negative: PIC 9P *) - else - match picture.category with - | Alphabetic _ | Boolean _ | National _ | Alphanumeric _ -> "" - | FloatNum _ -> "floatnum todo" - | FixedNum { digits; scale; with_sign; _ } - when not @@ is_edited picture -> - (if with_sign then "+" else "") - ^ simple_example_of ~digits ~scale ~with_dot:true value - | FixedNum { digits; scale; with_sign; - editions = { basics; floating; zerorepl } } -> - ignore (with_sign); - let is_negative = value < 0. in - let edit_zerorepl = Option.fold ~none:Fun.id - ~some:(fun { zero_replacement_symbol = symbol; - zero_replacement_ranges = ranges } -> - do_floatedit_n_zerorepl_on digits is_negative symbol ranges) - zerorepl in - let edit_floating = Option.fold ~none:Fun.id - ~some:(fun { floating_insertion_symbol = symbol; - floating_insertion_ranges = ranges } -> - do_floatedit_n_zerorepl_on digits is_negative symbol ranges) - floating in - try - (if Option.is_some floating - then "0" - else "") - ^ simple_example_of ~digits ~scale ~with_dot:false value - |> edit_basics ~is_negative:(value < 0.) basics - |> edit_zerorepl - |> edit_floating - with Invalid_argument e -> - Pretty.invalid_arg - "Unable to build example of picture, error '%s'" e - - - -let unit_tests = - [ ("99,B999,B000", 1234., "01, 234, 000"); - ("99,999", 12345., "12,345"); - ("999.99", 1.234, "001.23"); - ("999.99", 12.34, "012.34"); - ("999.99", 123.45, "123.45"); - ("999.99", 1234.5, "234.50"); - (* ("+999.99E+99", 12345., "+123.45E+02"); *) - ("999.99+", +6555.556, "555.55+"); - ("+9999.99", -6555.555, "-6555.55"); - ("9999.99", +1234.56, "1234.56"); - ("$999.99", -123.45, "$123.45"); - ("-$999.99", -123.456, "-$123.45"); - ("-$999.99", +123.456, " $123.45"); - ("$9999.99CR", +123.45, "$0123.45 "); - ("$9999.99DB", -123.45, "$0123.45DB"); - (* ("U999.99", -123.45, "EUR123.45"); *) - (* ("-u999.99", -123.456, "-USD123.45"); *) - ("$$$$.99", 0.123, " $.12"); - ("$$$9.99", 0.12, " $0.12"); - ("$,$$$,999.99", -1234.56, " $1,234.56"); - (* ("U,UUU,UU9.99-", -1234.56, "EUR1,234.56-"); *) - (* ("u,uuu,uu9.99", 1234.56, "USD1,234.56"); *) - ("+,+++,999.99", -123456.789, " -123,456.78"); - ("$$,$$$,$$$.99CR", -1234567., "$1,234,567.00CR"); - ("++,+++,+++.+++", 0000.00, " "); - ("$B++/+++.+", 0000.00, " "); - ("****.**", 0000.00, "****.**"); - ("ZZZZ.ZZ", 0000.00, " "); - ("ZZZZ.99", 0000.00, " .00"); - ("****.99", 0000.00, "****.00"); - ("ZZ99.99", 0000.00, " 00.00"); - ("Z,ZZZ.ZZ+", +123.456, " 123.45+"); - ("*,***.**+", -123.45, "**123.45-"); - ("**,***,***.**", +12345678.9, "12,345,678.90"); - (* ("$Z,ZZZ,ZZZ.ZZCR", +12345.67, "$ 12,345.67"); *) - ("$B*,***.**BBDB", 0., "*******.******"); - ("$B*,***,***.**BBDB", -12345.67, "$ ***12,345.67 DB");] - -let () = - let rec test = function - | [] -> () - | (pic, value, expected)::tl -> - match of_string config pic with - | Ok picture -> - let example = example_of ~picture value in - let error_msg = Pretty.to_string "ERROR: different result (%s, %f) -> '%s' (expected: '%s')" pic value example expected in - if String.equal example expected - then test tl - else failwith error_msg - | Error _ -> - failwith @@ Pretty.to_string "ERROR: Unable to form picture with picture-string '%s'\n" pic - in test unit_tests diff --git a/src/lsp/cobol_data/data_picture.mli b/src/lsp/cobol_data/data_picture.mli index 3d19f29bf..21131b17a 100644 --- a/src/lsp/cobol_data/data_picture.mli +++ b/src/lsp/cobol_data/data_picture.mli @@ -40,6 +40,9 @@ module TYPES: sig | Z | Zero + val pp_symbol: symbol Fmt.t + val pp_symbol_cobolized: symbol Fmt.t + type symbols = { symbol: symbol; @@ -90,7 +93,7 @@ module TYPES: sig and basic_edition = | SimpleInsertion of simple_insertion - | SpecialInsertion of int + | SpecialInsertion of special_insertion | FixedInsertion of fixed_insertion and simple_insertion = @@ -99,6 +102,11 @@ module TYPES: sig simple_insertion_offset: int; } + and special_insertion = + { + special_insertion_offset: int; + } + and fixed_insertion = { fixed_insertion_symbol: symbol; @@ -237,6 +245,3 @@ val pp_meaning_of_precedence_index stderr and returns `false` *) val unit_test : ?config:TYPES.config -> expect:string -> string -> bool - -val example_of - : picture:t -> float -> string diff --git a/src/lsp/cobol_lsp/cobol_lsp.ml b/src/lsp/cobol_lsp/cobol_lsp.ml index d94955588..b3c3a8438 100644 --- a/src/lsp/cobol_lsp/cobol_lsp.ml +++ b/src/lsp/cobol_lsp/cobol_lsp.ml @@ -36,6 +36,7 @@ module INTERNAL = struct module Document = Lsp_document module Server = Lsp_server module Loop = Lsp_server_loop + module Picture_interp = Lsp_picture_interp module Request = Lsp_request.INTERNAL module Utils = Lsp_utils module Debug = Lsp_debug diff --git a/src/lsp/cobol_lsp/lsp_data_info_printer.ml b/src/lsp/cobol_lsp/lsp_data_info_printer.ml index 47a237ffe..0cb2a0107 100644 --- a/src/lsp/cobol_lsp/lsp_data_info_printer.ml +++ b/src/lsp/cobol_lsp/lsp_data_info_printer.ml @@ -42,9 +42,9 @@ let pp_example_of ppf (picture: Cobol_data.Picture.t) = then string_of_int (int_of_float max) else string_of_float max in Fmt.pf ppf "\n\nExample display value:\n- 0: `%s`\n- %s: `%s`" - (Cobol_data.Picture.example_of ~picture 0.) + (Lsp_picture_interp.example_of ~picture 0.) max_str - (Cobol_data.Picture.example_of ~picture max) + (Lsp_picture_interp.example_of ~picture max) | _ -> () let pp_usage: usage Pretty.printer = diff --git a/src/lsp/cobol_lsp/lsp_picture_interp.ml b/src/lsp/cobol_lsp/lsp_picture_interp.ml new file mode 100644 index 000000000..47392d5c4 --- /dev/null +++ b/src/lsp/cobol_lsp/lsp_picture_interp.ml @@ -0,0 +1,263 @@ +(**************************************************************************) +(* *) +(* SuperBOL OSS Studio *) +(* *) +(* Copyright (c) 2022-2023 OCamlPro SAS *) +(* *) +(* All rights reserved. *) +(* This source code is licensed under the GNU Affero General Public *) +(* License version 3 found in the LICENSE.md file in the root directory *) +(* of this source tree. *) +(* *) +(**************************************************************************) + +open Cobol_data.Picture +open TYPES + + +let simple_insertion_char_of ~symbol = + match symbol with + | B -> ' ' + | Zero -> '0' + | Slant -> '/' + | DecimalSep -> '.' + | GroupingSep -> ',' + | _ -> Pretty.invalid_arg + "Not a simple insertion symbol '%a'" + pp_symbol symbol + +let fixed_insertion_str_of ~symbol ~is_negative = + match symbol with + | CS -> "$" + | Plus | Minus when is_negative -> "-" + | Plus -> "+" + | Minus -> " " + | CR | DB when not is_negative -> " " + | CR -> "CR" + | DB -> "DB" + | _ -> Pretty.invalid_arg + "Not a fixed insertion symbol '%a'" + pp_symbol_cobolized symbol + +let do_basic_edit_on ~is_negative basic s = + let (offset, insertion) = + match basic with + | SimpleInsertion + { simple_insertion_symbols = { symbol_occurences = n; symbol }; + simple_insertion_offset = offset } -> + offset, String.make n @@ simple_insertion_char_of ~symbol + | SpecialInsertion { special_insertion_offset = offset } -> + offset, "." + | FixedInsertion { fixed_insertion_symbol = symbol; fixed_insertion_offset = offset } -> + offset, fixed_insertion_str_of ~symbol ~is_negative + in + Str.string_before s offset + ^ insertion + ^ Str.string_after s offset + +let all_repl_indexes_from ~ranges s digits = + let indexes = + ranges + |> List.rev_map begin fun { floating_range_offset = offset; + floating_range_length = len } -> + List.init len (fun i -> offset + i) + end + |> List.flatten |> List.sort Int.compare + in + let is_only_repl_char = List.length indexes >= digits in + let min_index = List.hd indexes in + let (_, (all_indexes, all_zero)) = String.fold_left + begin fun ((idx, (acc_repl, should_continue_repl)) as acc) ch -> + if not should_continue_repl then acc else + if List.mem idx indexes + then (idx+1, + if ch == '0' + then (idx::acc_repl, true) + else (acc_repl, false)) + else + if min_index < idx && List.mem ch [' '; ','] + then (idx+1, (idx::acc_repl, true)) + else (idx+1, (acc_repl, should_continue_repl)) + end (0, ([], true)) s + in all_indexes, all_zero && is_only_repl_char + +let do_floatedit_n_zerorepl_on digits is_negative + symbol ranges s = + if ranges == [] then s else + let floating_last_ch = match symbol with + | Plus | Minus when is_negative -> '-' + | Plus -> '+' + | Minus -> ' ' + | CS -> '$' + | Z -> ' ' + | Star -> '*' + | _ -> Pretty.invalid_arg + "Floating edit or zero replacement symbol '%a' is invalid" + pp_symbol_cobolized symbol + in + let repl_ch = match symbol with + | Minus | Plus | CS | Z -> ' ' + | Star -> '*' + | _ -> Pretty.invalid_arg + "Floating edit or zero replacement symbol '%a' is invalid" + pp_symbol_cobolized symbol + in + let repl_str = String.make 1 repl_ch in + let all_repl_indexes, repl_everything = + all_repl_indexes_from ~ranges s digits in + if repl_everything + then + String.map begin fun ch -> + if ch == '.' && symbol == Star + then '.' + else repl_ch + end s + else + let (_, _, last_repl_idx, res) = String.fold_left + begin fun (i, after_decimal_point, last_repl_idx, res) ch -> + let orig_str = String.make 1 ch in + if after_decimal_point + then (i+1, after_decimal_point, last_repl_idx, res ^ orig_str) + else + if ch == '.' + then (i+1, true, last_repl_idx, res ^ ".") + else + if List.mem i all_repl_indexes + then (i+1, after_decimal_point, i, res ^ repl_str) + else (i+1, after_decimal_point, last_repl_idx, res ^ orig_str) + end (0, false, -1, "") s + in + String.mapi begin fun i ch -> + if i == last_repl_idx + then floating_last_ch + else ch + end + res + +let rec edit_basics ~is_negative basics s = + match basics with + | [] -> s + | hd::tl -> + do_basic_edit_on ~is_negative hd s + |> edit_basics ~is_negative tl + +let simple_example_of ~digits ~scale ~with_dot value = + let str_val = string_of_float (Float.abs value) in + let i = String.index str_val '.' in + let whole_part = Str.string_before str_val i in + let whole_len = String.length whole_part in + let floating_part = Str.string_after str_val (i+1) in + let required_len = digits - scale in + (String.init required_len + (fun i -> + if i < required_len - whole_len + then '0' + else whole_part.[i - (required_len - whole_len)]) + ) + ^ (if scale > 0 + then + (if with_dot then "." else "") + ^ String.init scale + (fun i -> + if i < String.length floating_part + then floating_part.[i] + else '0') + else "") + +let example_of ~picture value = + if List.exists (fun { symbol; _ } -> symbol == P) picture.pic + then "No example with P yet" (* /!\ scale can be negative: PIC 9P *) + else + match picture.category with + | Alphabetic _ | Boolean _ | National _ | Alphanumeric _ -> "" + | FloatNum _ -> "floatnum todo" + | FixedNum { digits; scale; with_sign; _ } + when not @@ is_edited picture -> + (if with_sign then "+" else "") + ^ simple_example_of ~digits ~scale ~with_dot:true value + | FixedNum { digits; scale; with_sign; + editions = { basics; floating; zerorepl } } -> + ignore (with_sign); + let is_negative = value < 0. in + let edit_zerorepl = Option.fold ~none:Fun.id + ~some:(fun { zero_replacement_symbol = symbol; + zero_replacement_ranges = ranges } -> + do_floatedit_n_zerorepl_on digits is_negative symbol ranges) + zerorepl in + let edit_floating = Option.fold ~none:Fun.id + ~some:(fun { floating_insertion_symbol = symbol; + floating_insertion_ranges = ranges } -> + do_floatedit_n_zerorepl_on digits is_negative symbol ranges) + floating in + try + (if Option.is_some floating + then "0" + else "") + ^ simple_example_of ~digits ~scale ~with_dot:false value + |> edit_basics ~is_negative:(value < 0.) basics + |> edit_zerorepl + |> edit_floating + with Invalid_argument e -> + Pretty.invalid_arg + "Unable to build example of picture, error '%s'" e + + +(* +let unit_tests = + [ ("99,B999,B000", 1234., "01, 234, 000"); + ("99,999", 12345., "12,345"); + ("999.99", 1.234, "001.23"); + ("999.99", 12.34, "012.34"); + ("999.99", 123.45, "123.45"); + ("999.99", 1234.5, "234.50"); + (* ("+999.99E+99", 12345., "+123.45E+02"); *) + ("999.99+", +6555.556, "555.55+"); + ("+9999.99", -6555.555, "-6555.55"); + ("9999.99", +1234.56, "1234.56"); + ("$999.99", -123.45, "$123.45"); + ("-$999.99", -123.456, "-$123.45"); + ("-$999.99", +123.456, " $123.45"); + ("$9999.99CR", +123.45, "$0123.45 "); + ("$9999.99DB", -123.45, "$0123.45DB"); + (* ("U999.99", -123.45, "EUR123.45"); *) + (* ("-u999.99", -123.456, "-USD123.45"); *) + ("$$$$.99", 0.123, " $.12"); + ("$$$9.99", 0.12, " $0.12"); + ("$,$$$,999.99", -1234.56, " $1,234.56"); + (* ("U,UUU,UU9.99-", -1234.56, "EUR1,234.56-"); *) + (* ("u,uuu,uu9.99", 1234.56, "USD1,234.56"); *) + ("+,+++,999.99", -123456.789, " -123,456.78"); + ("$$,$$$,$$$.99CR", -1234567., "$1,234,567.00CR"); + ("++,+++,+++.+++", 0000.00, " "); + ("$B++/+++.+", 0000.00, " "); + ("****.**", 0000.00, "****.**"); + ("ZZZZ.ZZ", 0000.00, " "); + ("ZZZZ.99", 0000.00, " .00"); + ("****.99", 0000.00, "****.00"); + ("ZZ99.99", 0000.00, " 00.00"); + ("Z,ZZZ.ZZ+", +123.456, " 123.45+"); + ("*,***.**+", -123.45, "**123.45-"); + ("**,***,***.**", +12345678.9, "12,345,678.90"); + (* ("$Z,ZZZ,ZZZ.ZZCR", +12345.67, "$ 12,345.67"); *) + ("$B*,***.**BBDB", 0., "*******.******"); + ("$B*,***,***.**BBDB", -12345.67, "$ ***12,345.67 DB");] + +module CHARS = Cobol_common.Basics.CharSet + +let () = + let config = { max_pic_length = 100; decimal_char = '.'; + currency_signs = CHARS.add '$' CHARS.empty } + in + let rec test = function + | [] -> () + | (pic, value, expected)::tl -> + match of_string config pic with + | Ok picture -> + let example = example_of ~picture value in + let error_msg = Pretty.to_string "ERROR: different result (%s, %f) -> '%s' (expected: '%s')" pic value example expected in + if String.equal example expected + then test tl + else failwith error_msg + | Error _ -> + failwith @@ Pretty.to_string "ERROR: Unable to form picture with picture-string '%s'\n" pic + in test unit_tests *) diff --git a/test/cobol_parsing/test_picture_parsing.ml b/test/cobol_parsing/test_picture_parsing.ml index 6a8bf3d12..9fa58a557 100644 --- a/test/cobol_parsing/test_picture_parsing.ml +++ b/test/cobol_parsing/test_picture_parsing.ml @@ -328,7 +328,7 @@ module Pictures = struct simple_insertion_offset = 0 }; SimpleInsertion { simple_insertion_symbols = comma 1; simple_insertion_offset = 5 }; - SpecialInsertion 9 ] + SpecialInsertion { special_insertion_offset = 9 } ] in { category = fixednum 9 3 ~basics; pic = [comma 2; nine 3; comma 1; nine 3; dot 1; nine 3] } @@ -360,7 +360,7 @@ module Pictures = struct and basics = [ SimpleInsertion { simple_insertion_symbols = comma 1; simple_insertion_offset = 3 }; - SpecialInsertion 7 ] + SpecialInsertion { special_insertion_offset = 7 } ] in { category = fixednum 9 3 ~basics ~zerorepl; pic = [z 3; comma 1; z 3; dot 1; z 3] } @@ -411,7 +411,7 @@ module Pictures = struct let basics = [ SimpleInsertion { simple_insertion_symbols = comma 1; simple_insertion_offset = 3 }; - SpecialInsertion 7 ] + SpecialInsertion { special_insertion_offset = 7 } ] in { category = floatnum 9 3 3 ~basics; pic = [nine 3; comma 1; nine 3; dot 1; nine 3; e 1; plus 1; nine 3] } @@ -443,7 +443,7 @@ module Pictures = struct let pic_ppvpp = let basics = - [ SpecialInsertion 2 ] + [ SpecialInsertion { special_insertion_offset = 2 } ] and floating = { floating_insertion_symbol = Plus; floating_insertion_ranges = [{ floating_range_offset = 0; diff --git a/test/lsp/lsp_picture_interp.ml b/test/lsp/lsp_picture_interp.ml new file mode 100644 index 000000000..2cdf121fc --- /dev/null +++ b/test/lsp/lsp_picture_interp.ml @@ -0,0 +1,74 @@ +(**************************************************************************) +(* *) +(* SuperBOL OSS Studio *) +(* *) +(* Copyright (c) 2022-2023 OCamlPro SAS *) +(* *) +(* All rights reserved. *) +(* This source code is licensed under the GNU Affero General Public *) +(* License version 3 found in the LICENSE.md file in the root directory *) +(* of this source tree. *) +(* *) +(**************************************************************************) + + +open Lsp_testing +module CHARS = Cobol_common.Basics.CharSet + +let unit_tests = + [ ("99,B999,B000", 1234., "01, 234, 000"); + ("99,999", 12345., "12,345"); + ("999.99", 1.234, "001.23"); + ("999.99", 12.34, "012.34"); + ("999.99", 123.45, "123.45"); + ("999.99", 1234.5, "234.50"); + (* ("+999.99E+99", 12345., "+123.45E+02"); *) + ("999.99+", +6555.556, "555.55+"); + ("+9999.99", -6555.555, "-6555.55"); + ("9999.99", +1234.56, "1234.56"); + ("$999.99", -123.45, "$123.45"); + ("-$999.99", -123.456, "-$123.45"); + ("-$999.99", +123.456, " $123.45"); + ("$9999.99CR", +123.45, "$0123.45 "); + ("$9999.99DB", -123.45, "$0123.45DB"); + (* ("U999.99", -123.45, "EUR123.45"); *) + (* ("-u999.99", -123.456, "-USD123.45"); *) + ("$$$$.99", 0.123, " $.12"); + ("$$$9.99", 0.12, " $0.12"); + ("$,$$$,999.99", -1234.56, " $1,234.56"); + (* ("U,UUU,UU9.99-", -1234.56, "EUR1,234.56-"); *) + (* ("u,uuu,uu9.99", 1234.56, "USD1,234.56"); *) + ("+,+++,999.99", -123456.789, " -123,456.78"); + ("$$,$$$,$$$.99CR", -1234567., "$1,234,567.00CR"); + ("++,+++,+++.+++", 0000.00, " "); + ("$B++/+++.+", 0000.00, " "); + ("****.**", 0000.00, "****.**"); + ("ZZZZ.ZZ", 0000.00, " "); + ("ZZZZ.99", 0000.00, " .00"); + ("****.99", 0000.00, "****.00"); + ("ZZ99.99", 0000.00, " 00.00"); + ("Z,ZZZ.ZZ+", +123.456, " 123.45+"); + ("*,***.**+", -123.45, "**123.45-"); + ("**,***,***.**", +12345678.9, "12,345,678.90"); + (* ("$Z,ZZZ,ZZZ.ZZCR", +12345.67, "$ 12,345.67"); *) + ("$B*,***.**BBDB", 0., "*******.******"); + ("$B*,***,***.**BBDB", -12345.67, "$ ***12,345.67 DB");] + +let () = + let config: Cobol_data.Picture.TYPES.config = + { max_pic_length = 100; decimal_char = '.'; + currency_signs = CHARS.add '$' CHARS.empty } + in + let rec test = function + | [] -> () + | (pic, value, expected)::tl -> + match Cobol_data.Picture.of_string config pic with + | Ok picture -> + let example = LSP.Picture_interp.example_of ~picture value in + let error_msg = Pretty.to_string "ERROR: different result (%s, %f) -> '%s' (expected: '%s')" pic value example expected in + if String.equal example expected + then test tl + else failwith error_msg + | Error _ -> + failwith @@ Pretty.to_string "ERROR: Unable to form picture with picture-string '%s'\n" pic + in test unit_tests From a1ba2efb1665ec65e0ee9eaf4538546566481e6d Mon Sep 17 00:00:00 2001 From: Mateo Date: Mon, 5 Aug 2024 16:22:04 +0200 Subject: [PATCH 4/7] fix: remove mutal exclusion of Z and CS The mutal exclusion of Z and CS as a floating insertion char is handled by the precedence grid --- src/lsp/cobol_data/data_picture.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lsp/cobol_data/data_picture.ml b/src/lsp/cobol_data/data_picture.ml index b1ac5d5eb..a890e1ebf 100644 --- a/src/lsp/cobol_data/data_picture.ml +++ b/src/lsp/cobol_data/data_picture.ml @@ -764,12 +764,11 @@ let char_order_checker_for_pic_string config = (* Maybe not in ISO/IEC 2014: Z/CS *) let mutual_exclusions = SymbolsMap.of_seq @@ List.to_seq [ - CS, Symbols.singleton Z; DecimalSep, Symbols.of_list [P; V]; P, Symbols.singleton DecimalSep; Star, Symbols.singleton Z; V, Symbols.singleton DecimalSep; - Z, Symbols.of_list [Star; CS]; + Z, Symbols.singleton Star; ] type exp_sequence_state = From 5c9083e61775598845607c32c4793674b95edef4 Mon Sep 17 00:00:00 2001 From: Mateo Date: Tue, 6 Aug 2024 12:42:35 +0200 Subject: [PATCH 5/7] chore: update CHANGELOG --- CHANGELOG.md | 1 + src/lsp/cobol_lsp/lsp_picture_interp.ml | 61 ------------------------- 2 files changed, 1 insertion(+), 61 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2335cb34f..d8ec308fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## [0.1.4] Next release ### Added +- Show display example of `NUMERIC-EDITED` data on hover [#337](https://github.com/OCamlPro/superbol-studio-oss/pull/337) - Support for dump and listing files, along with a task attribute for outputting the latter [#347](https://github.com/OCamlPro/superbol-studio-oss/pull/347) - Improved information shown on completion [#336](https://github.com/OCamlPro/superbol-studio-oss/pull/336) - Configuration flag for caching in storage provided by Visual Studio Code [#167](https://github.com/OCamlPro/superbol-studio-oss/pull/167) diff --git a/src/lsp/cobol_lsp/lsp_picture_interp.ml b/src/lsp/cobol_lsp/lsp_picture_interp.ml index 47392d5c4..66e61b438 100644 --- a/src/lsp/cobol_lsp/lsp_picture_interp.ml +++ b/src/lsp/cobol_lsp/lsp_picture_interp.ml @@ -200,64 +200,3 @@ let example_of ~picture value = with Invalid_argument e -> Pretty.invalid_arg "Unable to build example of picture, error '%s'" e - - -(* -let unit_tests = - [ ("99,B999,B000", 1234., "01, 234, 000"); - ("99,999", 12345., "12,345"); - ("999.99", 1.234, "001.23"); - ("999.99", 12.34, "012.34"); - ("999.99", 123.45, "123.45"); - ("999.99", 1234.5, "234.50"); - (* ("+999.99E+99", 12345., "+123.45E+02"); *) - ("999.99+", +6555.556, "555.55+"); - ("+9999.99", -6555.555, "-6555.55"); - ("9999.99", +1234.56, "1234.56"); - ("$999.99", -123.45, "$123.45"); - ("-$999.99", -123.456, "-$123.45"); - ("-$999.99", +123.456, " $123.45"); - ("$9999.99CR", +123.45, "$0123.45 "); - ("$9999.99DB", -123.45, "$0123.45DB"); - (* ("U999.99", -123.45, "EUR123.45"); *) - (* ("-u999.99", -123.456, "-USD123.45"); *) - ("$$$$.99", 0.123, " $.12"); - ("$$$9.99", 0.12, " $0.12"); - ("$,$$$,999.99", -1234.56, " $1,234.56"); - (* ("U,UUU,UU9.99-", -1234.56, "EUR1,234.56-"); *) - (* ("u,uuu,uu9.99", 1234.56, "USD1,234.56"); *) - ("+,+++,999.99", -123456.789, " -123,456.78"); - ("$$,$$$,$$$.99CR", -1234567., "$1,234,567.00CR"); - ("++,+++,+++.+++", 0000.00, " "); - ("$B++/+++.+", 0000.00, " "); - ("****.**", 0000.00, "****.**"); - ("ZZZZ.ZZ", 0000.00, " "); - ("ZZZZ.99", 0000.00, " .00"); - ("****.99", 0000.00, "****.00"); - ("ZZ99.99", 0000.00, " 00.00"); - ("Z,ZZZ.ZZ+", +123.456, " 123.45+"); - ("*,***.**+", -123.45, "**123.45-"); - ("**,***,***.**", +12345678.9, "12,345,678.90"); - (* ("$Z,ZZZ,ZZZ.ZZCR", +12345.67, "$ 12,345.67"); *) - ("$B*,***.**BBDB", 0., "*******.******"); - ("$B*,***,***.**BBDB", -12345.67, "$ ***12,345.67 DB");] - -module CHARS = Cobol_common.Basics.CharSet - -let () = - let config = { max_pic_length = 100; decimal_char = '.'; - currency_signs = CHARS.add '$' CHARS.empty } - in - let rec test = function - | [] -> () - | (pic, value, expected)::tl -> - match of_string config pic with - | Ok picture -> - let example = example_of ~picture value in - let error_msg = Pretty.to_string "ERROR: different result (%s, %f) -> '%s' (expected: '%s')" pic value example expected in - if String.equal example expected - then test tl - else failwith error_msg - | Error _ -> - failwith @@ Pretty.to_string "ERROR: Unable to form picture with picture-string '%s'\n" pic - in test unit_tests *) From dfafe2eae6e2b42d0aa2151719c6f1abdc9e3876 Mon Sep 17 00:00:00 2001 From: Mateo Date: Tue, 6 Aug 2024 17:19:39 +0200 Subject: [PATCH 6/7] fix: change space to nbsp to make them visible --- src/lsp/cobol_data/data_picture.mli | 4 +- src/lsp/cobol_lsp/lsp_data_info_printer.ml | 24 +++++++----- src/lsp/cobol_lsp/lsp_picture_interp.ml | 4 +- test/lsp/lsp_hover.ml | 44 ++++++---------------- 4 files changed, 29 insertions(+), 47 deletions(-) diff --git a/src/lsp/cobol_data/data_picture.mli b/src/lsp/cobol_data/data_picture.mli index 21131b17a..baba58387 100644 --- a/src/lsp/cobol_data/data_picture.mli +++ b/src/lsp/cobol_data/data_picture.mli @@ -40,8 +40,8 @@ module TYPES: sig | Z | Zero - val pp_symbol: symbol Fmt.t - val pp_symbol_cobolized: symbol Fmt.t + val pp_symbol: symbol Pretty.printer + val pp_symbol_cobolized: symbol Pretty.printer type symbols = { diff --git a/src/lsp/cobol_lsp/lsp_data_info_printer.ml b/src/lsp/cobol_lsp/lsp_data_info_printer.ml index 0cb2a0107..e530ad840 100644 --- a/src/lsp/cobol_lsp/lsp_data_info_printer.ml +++ b/src/lsp/cobol_lsp/lsp_data_info_printer.ml @@ -33,19 +33,23 @@ let max_value digits scale = let decimal_part = Str.string_before (Str.string_after s whole) scale in float_of_string (whole_part ^ "." ^ decimal_part) +let nbsp_repl = Str.global_replace (Str.regexp " ") " " + let pp_example_of ppf (picture: Cobol_data.Picture.t) = - match picture.category with - | FixedNum { digits; scale; _ } -> + try + match picture.category with + | FixedNum { digits; scale; _ } -> let max = max_value digits scale in let max_str = - if Float.is_integer max - then string_of_int (int_of_float max) - else string_of_float max in - Fmt.pf ppf "\n\nExample display value:\n- 0: `%s`\n- %s: `%s`" - (Lsp_picture_interp.example_of ~picture 0.) - max_str - (Lsp_picture_interp.example_of ~picture max) - | _ -> () + if Float.is_integer max + then string_of_int (int_of_float max) + else string_of_float max in + Fmt.pf ppf "\n\nExample display values [%s] (0) [%s] (%s)" + (Lsp_picture_interp.example_of ~picture 0. |> nbsp_repl) + (Lsp_picture_interp.example_of ~picture max |> nbsp_repl) + max_str + | _ -> () + with Invalid_argument _ -> () let pp_usage: usage Pretty.printer = let pp_usage_with_picture ppf name (picture: Cobol_data.Picture.t) = diff --git a/src/lsp/cobol_lsp/lsp_picture_interp.ml b/src/lsp/cobol_lsp/lsp_picture_interp.ml index 66e61b438..0c8b9c857 100644 --- a/src/lsp/cobol_lsp/lsp_picture_interp.ml +++ b/src/lsp/cobol_lsp/lsp_picture_interp.ml @@ -166,11 +166,11 @@ let simple_example_of ~digits ~scale ~with_dot value = let example_of ~picture value = if List.exists (fun { symbol; _ } -> symbol == P) picture.pic - then "No example with P yet" (* /!\ scale can be negative: PIC 9P *) + then raise @@ Invalid_argument "No example with P yet" (* /!\ scale can be negative: PIC 9P *) else match picture.category with | Alphabetic _ | Boolean _ | National _ | Alphanumeric _ -> "" - | FloatNum _ -> "floatnum todo" + | FloatNum _ -> raise @@ Invalid_argument "No example for floatnum yet" | FixedNum { digits; scale; with_sign; _ } when not @@ is_edited picture -> (if with_sign then "+" else "") diff --git a/test/lsp/lsp_hover.ml b/test/lsp/lsp_hover.ml index 1651d1072..d4452f56c 100644 --- a/test/lsp/lsp_hover.ml +++ b/test/lsp/lsp_hover.ml @@ -292,9 +292,7 @@ let%expect_test "hover-typedef-vars" = PIC 999 USAGE DISPLAY ``` NUMERIC(digits = 3, scale = 0, with_sign = false) - Example display value: - - 0: `000` - - 123: `123` + Example display values [000] (0) [123] (123) VALUE 123 (line 8, character 16): __rootdir__/prog.cob:9.13-9.21: @@ -393,9 +391,7 @@ let%expect_test "hover-typedef-vars" = PIC 999 USAGE DISPLAY ``` NUMERIC(digits = 3, scale = 0, with_sign = false) - Example display value: - - 0: `000` - - 123: `123` + Example display values [000] (0) [123] (123) VALUE 123 (line 12, character 47): __rootdir__/prog.cob:13.44-13.52: @@ -469,9 +465,7 @@ let%expect_test "hover-typedef-vars-usage" = PIC -BZZZ,ZZ9.99 USAGE DISPLAY ``` NUMERIC(digits = 8, scale = 2, with_sign = false) - Example display value: - - 0: ` 0.00` - - 123456.78: ` 123,456.78` + Example display values [        0.00] (0) [  123,456.78] (123456.78) (line 6, character 11): __rootdir__/prog.cob:7.11-7.15: 4 DATA DIVISION. @@ -488,9 +482,7 @@ let%expect_test "hover-typedef-vars-usage" = PIC 9 USAGE BINARY ``` NUMERIC(digits = 1, scale = 0, with_sign = false) - Example display value: - - 0: `0` - - 1: `1` + Example display values [0] (0) [1] (1) (line 7, character 11): __rootdir__/prog.cob:8.11-8.15: 5 WORKING-STORAGE SECTION. @@ -566,9 +558,7 @@ let%expect_test "hover-typedef-vars-usage" = PIC 9 USAGE PACKED-DECIMAL ``` NUMERIC(digits = 1, scale = 0, with_sign = false) - Example display value: - - 0: `0` - - 1: `1` + Example display values [0] (0) [1] (1) (line 12, character 11): __rootdir__/prog.cob:13.11-13.15: 10 01 VAR6 PIC 111 USAGE BIT. @@ -585,9 +575,7 @@ let%expect_test "hover-typedef-vars-usage" = PIC $++/+.+B+ USAGE DISPLAY ``` NUMERIC(digits = 4, scale = 2, with_sign = false) - Example display value: - - 0: ` ` - - 12.34: `$+1/2.3 4` |}];; + Example display values [         ] (0) [$+1/2.3 4] (12.34) |}];; let%expect_test "hover-typedef-filler-vars" = let { projdir; end_with_postproc }, server = make_lsp_project () in @@ -825,9 +813,7 @@ let%expect_test "hover-typedef-renames" = PIC 9 USAGE DISPLAY ``` NUMERIC(digits = 1, scale = 0, with_sign = false) - Example display value: - - 0: `0` - - 1: `1` + Example display values [0] (0) [1] (1) (line 8, character 16): __rootdir__/prog.cob:9.10-9.25: 6 01 X. @@ -845,9 +831,7 @@ let%expect_test "hover-typedef-renames" = PIC 9 USAGE DISPLAY ``` NUMERIC(digits = 1, scale = 0, with_sign = false) - Example display value: - - 0: `0` - - 1: `1` + Example display values [0] (0) [1] (1) (line 8, character 23): __rootdir__/prog.cob:9.23-9.24: 6 01 X. @@ -864,9 +848,7 @@ let%expect_test "hover-typedef-renames" = PIC 9 USAGE DISPLAY ``` NUMERIC(digits = 1, scale = 0, with_sign = false) - Example display value: - - 0: `0` - - 1: `1` + Example display values [0] (0) [1] (1) (line 9, character 22): __rootdir__/prog.cob:10.13-10.22: 7 05 Y PIC 9. @@ -902,9 +884,7 @@ let%expect_test "hover-typedef-renames" = PIC 9 USAGE DISPLAY ``` NUMERIC(digits = 1, scale = 0, with_sign = false) - Example display value: - - 0: `0` - - 1: `1` |}];; + Example display values [0] (0) [1] (1) |}];; let%expect_test "hover-typedef-redefines" = let { projdir; end_with_postproc }, server = make_lsp_project () in @@ -959,9 +939,7 @@ let%expect_test "hover-typedef-redefines" = PIC 9 USAGE DISPLAY ``` NUMERIC(digits = 1, scale = 0, with_sign = false) - Example display value: - - 0: `0` - - 1: `1` + Example display values [0] (0) [1] (1) (line 9, character 20): __rootdir__/prog.cob:10.20-10.21: 7 05 Y PIC 9. From f60f2f5c164eeee24666f8556616aa381a54fd8e Mon Sep 17 00:00:00 2001 From: Mateo Date: Thu, 8 Aug 2024 09:59:16 +0200 Subject: [PATCH 7/7] feat: slightly change format, replace   with utf8 nbsp --- src/lsp/cobol_lsp/lsp_data_info_printer.ml | 5 +++-- test/lsp/lsp_hover.ml | 22 +++++++++++----------- test/lsp/lsp_picture_interp.ml | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/lsp/cobol_lsp/lsp_data_info_printer.ml b/src/lsp/cobol_lsp/lsp_data_info_printer.ml index e530ad840..1c62761db 100644 --- a/src/lsp/cobol_lsp/lsp_data_info_printer.ml +++ b/src/lsp/cobol_lsp/lsp_data_info_printer.ml @@ -33,7 +33,7 @@ let max_value digits scale = let decimal_part = Str.string_before (Str.string_after s whole) scale in float_of_string (whole_part ^ "." ^ decimal_part) -let nbsp_repl = Str.global_replace (Str.regexp " ") " " +let nbsp_repl = Str.global_replace (Str.regexp " ") " " (* <- utf8 nbsp *) let pp_example_of ppf (picture: Cobol_data.Picture.t) = try @@ -44,11 +44,12 @@ let pp_example_of ppf (picture: Cobol_data.Picture.t) = if Float.is_integer max then string_of_int (int_of_float max) else string_of_float max in - Fmt.pf ppf "\n\nExample display values [%s] (0) [%s] (%s)" + Fmt.pf ppf "\n\n*e.g,* [`%s`] (0), [`%s`] (%s)" (Lsp_picture_interp.example_of ~picture 0. |> nbsp_repl) (Lsp_picture_interp.example_of ~picture max |> nbsp_repl) max_str | _ -> () + with Invalid_argument _ -> () let pp_usage: usage Pretty.printer = diff --git a/test/lsp/lsp_hover.ml b/test/lsp/lsp_hover.ml index d4452f56c..7ee599d0e 100644 --- a/test/lsp/lsp_hover.ml +++ b/test/lsp/lsp_hover.ml @@ -292,7 +292,7 @@ let%expect_test "hover-typedef-vars" = PIC 999 USAGE DISPLAY ``` NUMERIC(digits = 3, scale = 0, with_sign = false) - Example display values [000] (0) [123] (123) + *e.g,* [`000`] (0), [`123`] (123) VALUE 123 (line 8, character 16): __rootdir__/prog.cob:9.13-9.21: @@ -391,7 +391,7 @@ let%expect_test "hover-typedef-vars" = PIC 999 USAGE DISPLAY ``` NUMERIC(digits = 3, scale = 0, with_sign = false) - Example display values [000] (0) [123] (123) + *e.g,* [`000`] (0), [`123`] (123) VALUE 123 (line 12, character 47): __rootdir__/prog.cob:13.44-13.52: @@ -465,7 +465,7 @@ let%expect_test "hover-typedef-vars-usage" = PIC -BZZZ,ZZ9.99 USAGE DISPLAY ``` NUMERIC(digits = 8, scale = 2, with_sign = false) - Example display values [        0.00] (0) [  123,456.78] (123456.78) + *e.g,* [`        0.00`] (0), [`  123,456.78`] (123456.78) (line 6, character 11): __rootdir__/prog.cob:7.11-7.15: 4 DATA DIVISION. @@ -482,7 +482,7 @@ let%expect_test "hover-typedef-vars-usage" = PIC 9 USAGE BINARY ``` NUMERIC(digits = 1, scale = 0, with_sign = false) - Example display values [0] (0) [1] (1) + *e.g,* [`0`] (0), [`1`] (1) (line 7, character 11): __rootdir__/prog.cob:8.11-8.15: 5 WORKING-STORAGE SECTION. @@ -558,7 +558,7 @@ let%expect_test "hover-typedef-vars-usage" = PIC 9 USAGE PACKED-DECIMAL ``` NUMERIC(digits = 1, scale = 0, with_sign = false) - Example display values [0] (0) [1] (1) + *e.g,* [`0`] (0), [`1`] (1) (line 12, character 11): __rootdir__/prog.cob:13.11-13.15: 10 01 VAR6 PIC 111 USAGE BIT. @@ -575,7 +575,7 @@ let%expect_test "hover-typedef-vars-usage" = PIC $++/+.+B+ USAGE DISPLAY ``` NUMERIC(digits = 4, scale = 2, with_sign = false) - Example display values [         ] (0) [$+1/2.3 4] (12.34) |}];; + *e.g,* [`         `] (0), [`$+1/2.3 4`] (12.34) |}];; let%expect_test "hover-typedef-filler-vars" = let { projdir; end_with_postproc }, server = make_lsp_project () in @@ -813,7 +813,7 @@ let%expect_test "hover-typedef-renames" = PIC 9 USAGE DISPLAY ``` NUMERIC(digits = 1, scale = 0, with_sign = false) - Example display values [0] (0) [1] (1) + *e.g,* [`0`] (0), [`1`] (1) (line 8, character 16): __rootdir__/prog.cob:9.10-9.25: 6 01 X. @@ -831,7 +831,7 @@ let%expect_test "hover-typedef-renames" = PIC 9 USAGE DISPLAY ``` NUMERIC(digits = 1, scale = 0, with_sign = false) - Example display values [0] (0) [1] (1) + *e.g,* [`0`] (0), [`1`] (1) (line 8, character 23): __rootdir__/prog.cob:9.23-9.24: 6 01 X. @@ -848,7 +848,7 @@ let%expect_test "hover-typedef-renames" = PIC 9 USAGE DISPLAY ``` NUMERIC(digits = 1, scale = 0, with_sign = false) - Example display values [0] (0) [1] (1) + *e.g,* [`0`] (0), [`1`] (1) (line 9, character 22): __rootdir__/prog.cob:10.13-10.22: 7 05 Y PIC 9. @@ -884,7 +884,7 @@ let%expect_test "hover-typedef-renames" = PIC 9 USAGE DISPLAY ``` NUMERIC(digits = 1, scale = 0, with_sign = false) - Example display values [0] (0) [1] (1) |}];; + *e.g,* [`0`] (0), [`1`] (1) |}];; let%expect_test "hover-typedef-redefines" = let { projdir; end_with_postproc }, server = make_lsp_project () in @@ -939,7 +939,7 @@ let%expect_test "hover-typedef-redefines" = PIC 9 USAGE DISPLAY ``` NUMERIC(digits = 1, scale = 0, with_sign = false) - Example display values [0] (0) [1] (1) + *e.g,* [`0`] (0), [`1`] (1) (line 9, character 20): __rootdir__/prog.cob:10.20-10.21: 7 05 Y PIC 9. diff --git a/test/lsp/lsp_picture_interp.ml b/test/lsp/lsp_picture_interp.ml index 2cdf121fc..94123eec1 100644 --- a/test/lsp/lsp_picture_interp.ml +++ b/test/lsp/lsp_picture_interp.ml @@ -50,7 +50,7 @@ let unit_tests = ("Z,ZZZ.ZZ+", +123.456, " 123.45+"); ("*,***.**+", -123.45, "**123.45-"); ("**,***,***.**", +12345678.9, "12,345,678.90"); - (* ("$Z,ZZZ,ZZZ.ZZCR", +12345.67, "$ 12,345.67"); *) + ("$Z,ZZZ,ZZZ.ZZCR", +12345.67, "$ 12,345.67 "); ("$B*,***.**BBDB", 0., "*******.******"); ("$B*,***,***.**BBDB", -12345.67, "$ ***12,345.67 DB");]