diff --git a/fix-engine/src-core-pp/parser_utils.iml b/fix-engine/src-core-pp/parser_utils.iml index a88a52b3..48f10df1 100644 --- a/fix-engine/src-core-pp/parser_utils.iml +++ b/fix-engine/src-core-pp/parser_utils.iml @@ -12,9 +12,9 @@ a stream of key=value pairs and then into a stream of messages. *) [@@@program] +[@@@import "../src-core-utils-msg/parse_message_util.iml"] +[@@@import "../src-core-utils-msg/parser_combinators.iml"] [@@@import "parse_base_types.iml"] -[@@@require "fix-engine.util-msg"] - (** Split [str] into pairs of [key/value] separated by [sep], each pair being a [key=value] pair. *) diff --git a/fix-engine/src-core-utils-msg/dune b/fix-engine/src-core-utils-msg/dune index 81b49694..cd736d72 100644 --- a/fix-engine/src-core-utils-msg/dune +++ b/fix-engine/src-core-utils-msg/dune @@ -5,4 +5,4 @@ (modules message parse_message_util parser_combinators) (preprocess (pps ppx_deriving.std)) - (libraries)) + (libraries imandra-prelude)) diff --git a/fix-engine/src-core-utils-msg/message.ml b/fix-engine/src-core-utils-msg/message.iml similarity index 82% rename from fix-engine/src-core-utils-msg/message.ml rename to fix-engine/src-core-utils-msg/message.iml index c012fd94..ebfe7d6b 100644 --- a/fix-engine/src-core-utils-msg/message.ml +++ b/fix-engine/src-core-utils-msg/message.iml @@ -1,8 +1,12 @@ +[@@@program] + +open Caml + type t = (string * string) list [@@deriving show] -let checksum_string (s : string) : int = - let n = ref 0 in - for i = String.length s - 1 downto 0 do +let checksum_string (s : string) : Int.t = + let n = ref 0i in + for i = String.length s - 1i downto 0i do n := !n + Char.code (String.unsafe_get s i) done; !n @@ -10,11 +14,11 @@ let checksum_string (s : string) : int = let checksum (msg : t) = let rec scan n msg = match msg with - | ("10", _v) :: _tl -> n mod 256 - | (k, v) :: tl -> scan (checksum_string k + checksum_string v + 62 + n) tl - | [] -> n mod 256 + | ("10", _v) :: _tl -> n mod 256i + | (k, v) :: tl -> scan (checksum_string k + checksum_string v + 62i + n) tl + | [] -> n mod 256i in - scan 0 msg + scan 0i msg let do_check_checksum = ref true @@ -44,13 +48,15 @@ let valid_body_length (msg : (string * string) list) : bool = match msg with | ("8", _) :: tl | ("9", _) :: tl -> scan n tl | ("10", _) :: _tl -> n - | (k, v) :: tl -> n + scan String.(length k + length v + 2) tl + | (k, v) :: tl -> n + scan String.(length k + length v + 2i) tl | [] -> n in - scan 0 msg + scan 0i msg in match msg with | ("8", _) :: ("9", bl) :: _ -> (try body_length = int_of_string bl with _ -> false) | _ -> false ) + +[@@@logic] diff --git a/fix-engine/src-core-utils-msg/message.mli b/fix-engine/src-core-utils-msg/message.mli deleted file mode 100644 index 9644b10d..00000000 --- a/fix-engine/src-core-utils-msg/message.mli +++ /dev/null @@ -1,22 +0,0 @@ -type t = (string * string) list [@@deriving show] - -val checksum_string : string -> int - -val do_check_checksum : bool ref - -val valid_checksum : t -> bool -(** Verifies the checksum (sum of all bytes mod 256) of all bytes in - the message up to the CheckSum<10> entry. Returns true if the computed - checksum is equal to the value of CheckSum<10>. -*) - -val checksum : t -> int -(** A checksum in [[0..<256]] *) - -val do_check_body_length : bool ref - -val valid_body_length : t -> bool -(** Checks that the message contains BodyLength<9> field as a second entry - in the message. And that the value equals to the number of bytes between - BodyLength<9> and CheckSum<10> entries in the message. -*) diff --git a/fix-engine/src-core-utils-msg/parse_message_util.ml b/fix-engine/src-core-utils-msg/parse_message_util.iml similarity index 82% rename from fix-engine/src-core-utils-msg/parse_message_util.ml rename to fix-engine/src-core-utils-msg/parse_message_util.iml index 0720e319..ab3ee463 100644 --- a/fix-engine/src-core-utils-msg/parse_message_util.ml +++ b/fix-engine/src-core-utils-msg/parse_message_util.iml @@ -1,6 +1,10 @@ +[@@@program] + +open Caml + let split_into_key_value (sep : char) (str : string) : (string * string) Seq.t = (* find the next split char in the internal buffer *) - let next_split i : int option = + let next_split i : Int.t option = match String.index_from_opt str i sep with | Some n when n >= String.length str -> None | r -> r @@ -20,12 +24,12 @@ let split_into_key_value (sep : char) (str : string) : (string * string) Seq.t = match String.index_from_opt str i '=' with | Some idx_eq when idx_eq < end_of_kv -> let k = String.sub str i (idx_eq - i) in - let v = String.sub str (idx_eq + 1) (end_of_kv - idx_eq - 1) in - Seq.Cons ((k, v), loop ~is_done ~i:(end_of_kv + 1)) + let v = String.sub str (idx_eq + 1i) (end_of_kv - idx_eq - 1i) in + Seq.Cons ((k, v), loop ~is_done ~i:(end_of_kv + 1i)) | _ -> Seq.Nil ) in - loop ~is_done:false ~i:0 + loop ~is_done:false ~i:0i let split_into_messages (seq : (string * string) Seq.t) : (string * string) list Seq.t = @@ -40,3 +44,5 @@ let split_into_messages (seq : (string * string) Seq.t) : loop ((k, v) :: acc) tail () in loop [] seq + +[@@@logic] diff --git a/fix-engine/src-core-utils-msg/parse_message_util.mli b/fix-engine/src-core-utils-msg/parse_message_util.mli deleted file mode 100644 index 4f663560..00000000 --- a/fix-engine/src-core-utils-msg/parse_message_util.mli +++ /dev/null @@ -1,8 +0,0 @@ -(** Utilities to parse messages *) - -val split_into_key_value : char -> string -> (string * string) Seq.t -(** [split_into_key_value sep str] splits the string into [key=value] - pairs, separated by [sep]. *) - -val split_into_messages : (string * string) Seq.t -> Message.t Seq.t -(** Split a stream of key/value pairs into full messages, terminated by "10" *) diff --git a/fix-engine/src-core-utils-msg/parser_combinators.ml b/fix-engine/src-core-utils-msg/parser_combinators.iml similarity index 91% rename from fix-engine/src-core-utils-msg/parser_combinators.ml rename to fix-engine/src-core-utils-msg/parser_combinators.iml index c2fa68e5..cf78bdc6 100644 --- a/fix-engine/src-core-utils-msg/parser_combinators.ml +++ b/fix-engine/src-core-utils-msg/parser_combinators.iml @@ -1,4 +1,28 @@ (* rewriting of Parser_utils *) +(** Parser combinators for FIX messages. + + + Each combinator has access to the message and can query key/values. + + A typical message parser would look like this. + + {[ + let parse_message = Parser_combinators.( + let* group = repeating "1" parse_rg in + let* () = check_duplicate_tags msg in + let* block = block parse_blk + and* x = req "2" parse_int + and* y = opt "3" parse_string + and* z = req "4" parse_int in + let+ () = check_unknown_tags in + \{ block; group; x; y; z \} + ) + ]} +*) +[@@@program] +[@@@import "message.iml"] + +open Caml module Str_set = Set.Make (String) @@ -37,7 +61,6 @@ let pp_error fmt e = type nonrec 'a result = ('a, error) result (** Result of parsing *) -open struct (* Extract a value given a tag, returns a pair with the value and the rest of the list.*) let take (key : string) (lst : (string * string) list) = let rec take accu = function @@ -71,7 +94,6 @@ open struct | [] -> List.rev accu, [] in split [] msg -end (* internal state during parsing *) type state_ = { mutable msg: Message.t } @@ -230,7 +252,7 @@ let check_unknown_tags : _ t = )); } -let parse_int = int_of_string_opt +let parse_int s = int_of_string_opt s |> Option.map Z.of_int let repeating tag ~(block_parser : 'a t) : (_ * 'a list) t = { @@ -251,7 +273,7 @@ let repeating tag ~(block_parser : 'a t) : (_ * 'a list) t = (* Break the list into a list of lists using the separator *) let groups : msg list = cut_on_separator groups_msg in (* Check that the length is correct *) - if List.length groups <> numInGroup then + if List.length groups <> Z.to_int numInGroup then fail_ (IncorrectNumInGroupCount tag) else ( (* Pass each list into the block parser ( reverses the list ) *) @@ -285,3 +307,5 @@ let repeating tag ~(block_parser : 'a t) : (_ * 'a list) t = let run (self : _ t) (msg : msg) : _ result = let st = { msg } in try Ok (self.run st) with Fail e -> Error e + +[@@@logic] diff --git a/fix-engine/src-core-utils-msg/parser_combinators.mli b/fix-engine/src-core-utils-msg/parser_combinators.mli deleted file mode 100644 index 52c802d6..00000000 --- a/fix-engine/src-core-utils-msg/parser_combinators.mli +++ /dev/null @@ -1,106 +0,0 @@ -(** Parser combinators for FIX messages. - - - Each combinator has access to the message and can query key/values. - - A typical message parser would look like this. - - {[ - let parse_message = Parser_combinators.( - let* group = repeating "1" parse_rg in - let* () = check_duplicate_tags msg in - let* block = block parse_blk - and* x = req "2" parse_int - and* y = opt "3" parse_string - and* z = req "4" parse_int in - let+ () = check_unknown_tags in - \{ block; group; x; y; z \} - ) - ]} -*) - -type msg = Message.t - -type tag = string - -type error = - | UnknownMessageTag of string - | RequiredTagMissing of string - | DuplicateTag of string - | WrongValueFormat of string - | UndefinedTag of string - | EmptyValue of string - | IncorrectNumInGroupCount of string - | RepeatingGroupOutOfOrder of string - | GarbledMessage - -val error_to_string : error -> string - -val pp_error : Format.formatter -> error -> unit - -type +'a t - -type +'a value_parser = string -> 'a option - -val or_raw_fix : 'a value_parser -> ('a, string) result value_parser -(** A parser that never fails. *) - -type nonrec 'a result = ('a, error) result -(** Result of parsing *) - -val run : 'a t -> msg -> 'a result - -val return : 'a -> 'a t - -val fail : error -> _ t - -val get_msg : msg t -(** Get current message *) - -val set_msg : msg -> unit t -(** Set current message *) - -val reflect_err : 'a t -> 'a result t -(** [reflect_err p] captures errors occurring in [p] and reflects them - into the returned value. *) - -val req : tag -> 'a value_parser -> 'a t -(** Required field *) - -val opt : tag -> 'a value_parser -> 'a option t -(** Optional field *) - -val repeating : tag -> block_parser:'a t -> (int option * 'a list) t -(** Repeating group parser combinator, starting from the tag that encodes - the length of the list, identifies the subgroups and passes each of them - into the block parser. Performs a number of consistency checks on the parsed - results. -*) - -val check_duplicate_tags : unit t -(** Checks for duplicate tags in messages, and fail if any is found - with [DuplicateTag _]. - Should be called after all the repeating groups are processed. *) - -val do_check_unknown_tags : bool ref - -val check_unknown_tags : unit t -(** Checks for remaining tags that have not been processed, and - fail if one is found with [UndefinedTag _]. Should be called - at the end just before returning the result. *) - -module Infix : sig - val ( >|= ) : 'a t -> ('a -> 'b) -> 'b t - - val ( >>= ) : 'a t -> ('a -> 'b t) -> 'b t - - val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t - - val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t - - val ( and+ ) : 'a t -> 'b t -> ('a * 'b) t - - val ( and* ) : 'a t -> 'b t -> ('a * 'b) t -end - -include module type of Infix diff --git a/fix-engine/src-protocol-pp/encode_full_messages.iml b/fix-engine/src-protocol-pp/encode_full_messages.iml index 31672a44..a2a34baf 100644 --- a/fix-engine/src-protocol-pp/encode_full_messages.iml +++ b/fix-engine/src-protocol-pp/encode_full_messages.iml @@ -7,8 +7,6 @@ parse_full_messages.ml *) -[@@@require "fix-engine.util-msg"] - [@@@import "encode_full_tags.iml"] [@@@import "encode_admin_messages.iml"] @@ -17,6 +15,8 @@ [@@@import "../src-core-pp/encode_base_types.iml"] +[@@@import "../src-core-utils-msg/message.iml"] + [@@@program] open Full_messages diff --git a/fix-engine/src-protocol-pp/parse_admin_enums.iml b/fix-engine/src-protocol-pp/parse_admin_enums.iml index d53c5b57..2aae4ad5 100644 --- a/fix-engine/src-protocol-pp/parse_admin_enums.iml +++ b/fix-engine/src-protocol-pp/parse_admin_enums.iml @@ -7,7 +7,7 @@ *) [@@@import "../src-protocol/full_admin_enums.iml"] -[@@@require "fix-engine.util-msg"] +[@@@import "../src-core-utils-msg/parser_combinators.iml"] [@@@program] diff --git a/fix-engine/src-protocol-pp/parse_admin_messages.iml b/fix-engine/src-protocol-pp/parse_admin_messages.iml index beb382ec..9865928b 100644 --- a/fix-engine/src-protocol-pp/parse_admin_messages.iml +++ b/fix-engine/src-protocol-pp/parse_admin_messages.iml @@ -7,7 +7,6 @@ parse_admin_messages.ml *) -[@@@require "fix-engine.util-msg"] [@@@import "../src-protocol/full_admin_tags.iml"] [@@@import "../src-protocol/full_admin_messages.iml"] [@@@import "../src-core-pp/parse_base_types.iml"] @@ -15,6 +14,7 @@ [@@@import "parse_admin_enums.iml"] [@@@import "parse_full_tags.iml"] [@@@import "../src-core/datetime.iml"] +[@@@import "../src-core-utils-msg/parser_combinators.iml"] [@@@program] open Full_admin_tags;; open Full_admin_messages;; diff --git a/fix-engine/src-protocol-pp/parse_admin_tags.iml b/fix-engine/src-protocol-pp/parse_admin_tags.iml index b9666525..0598447a 100644 --- a/fix-engine/src-protocol-pp/parse_admin_tags.iml +++ b/fix-engine/src-protocol-pp/parse_admin_tags.iml @@ -8,7 +8,7 @@ *) [@@@import "../src-protocol/full_admin_tags.iml"] -[@@@require "fix-engine.util-msg"] +[@@@import "../src-core-utils-msg/parser_combinators.iml"] [@@@program] open Full_admin_tags;; diff --git a/fix-engine/src-protocol-pp/parse_full_messages.iml b/fix-engine/src-protocol-pp/parse_full_messages.iml index c9644e9c..4eb70b06 100644 --- a/fix-engine/src-protocol-pp/parse_full_messages.iml +++ b/fix-engine/src-protocol-pp/parse_full_messages.iml @@ -7,14 +7,14 @@ parse_full_messages.ml *) -[@@@require "fix-engine.util-msg"] - [@@@import "../src-protocol/full_message_tags.iml"] [@@@import "../src-protocol/full_messages.iml"] [@@@import "../src-core-pp/parse_base_types.iml"] +[@@@import "../src-core-utils-msg/parser_combinators.iml"] + [@@@import "parse_full_tags.iml"] [@@@import "parse_admin_messages.iml"]