···148148 | Pair : 'a t * 'b t -> ('a * 'b) t
149149 | Triple : 'a t * 'b t * 'c t -> ('a * 'b * 'c) t
150150151151-and 'a vcase = {
152152- tag : string;
153153- inject : Sexp.t list -> ('a, codec_error) result;
154154- project : 'a -> Sexp.t list option;
155155-}
151151+and _ vcase =
152152+ | Vcase0 : { tag : string; value : 'a; project : 'a -> bool } -> 'a vcase
153153+ | Vcase1 : {
154154+ tag : string;
155155+ codec : 'b t;
156156+ inject : 'b -> 'a;
157157+ project : 'a -> 'b option;
158158+ }
159159+ -> 'a vcase
156160157161(* ---- Kind/doc accessors ---- *)
158162···397401 | None -> value_error ("unknown enum value: " ^ s))
398402 | _ -> type_error ~expected:"atom" v
399403404404+and vcase_tag : type a. a vcase -> string = function
405405+ | Vcase0 c -> c.tag
406406+ | Vcase1 c -> c.tag
407407+400408and decode_variant : type a. a vcase list -> Sexp.t -> (a, codec_error) result =
401409 fun cases v ->
402402- let case_map = List.map (fun c -> (c.tag, c)) cases in
410410+ let case_map = List.map (fun c -> (vcase_tag c, c)) cases in
403411 match v with
404412 | Sexp.Atom tag -> (
405413 match List.assoc_opt tag case_map with
406406- | Some case -> case.inject []
414414+ | Some (Vcase0 c) -> Ok c.value
415415+ | Some (Vcase1 _) -> value_error ("expected arguments for variant " ^ tag)
407416 | None -> value_error ("unknown variant tag: " ^ tag))
408417 | Sexp.List (Sexp.Atom tag :: args) -> (
409418 match List.assoc_opt tag case_map with
410410- | Some case -> case.inject args
419419+ | Some (Vcase0 c) ->
420420+ if args = [] then Ok c.value
421421+ else value_error ("unexpected arguments for nullary variant " ^ tag)
422422+ | Some (Vcase1 c) -> (
423423+ let sexp_val = match args with [ v ] -> v | vs -> Sexp.List vs in
424424+ match of_sexp c.codec sexp_val with
425425+ | Ok v -> Ok (c.inject v)
426426+ | Error e -> Error e)
411427 | None -> value_error ("unknown variant tag: " ^ tag))
412428 | _ -> type_error ~expected:"variant (tag ...)" v
413429···486502 fun cases v ->
487503 let rec find = function
488504 | [] -> failwith "no matching variant case"
489489- | case :: rest -> (
490490- match case.project v with
491491- | Some [] -> Sexp.Atom case.tag
492492- | Some args -> Sexp.List (Sexp.Atom case.tag :: args)
505505+ | Vcase0 c :: rest -> if c.project v then Sexp.Atom c.tag else find rest
506506+ | Vcase1 c :: rest -> (
507507+ match c.project v with
508508+ | Some x -> (
509509+ let encoded = to_sexp c.codec x in
510510+ match encoded with
511511+ | Sexp.List args -> Sexp.List (Sexp.Atom c.tag :: args)
512512+ | _ -> Sexp.List [ Sexp.Atom c.tag; encoded ])
493513 | None -> find rest)
494514 in
495515 find cases
···891911 type 'a codec = 'a t
892912 type 'a case = 'a vcase
893913894894- let case0 tag inject project =
895895- {
896896- tag;
897897- inject = (fun _ -> Ok inject);
898898- project = (fun v -> if project v then Some [] else None);
899899- }
900900-901901- let case1 tag (codec : 'b t) inject project =
902902- {
903903- tag;
904904- inject =
905905- (function
906906- | [ v ] -> Result.map inject (of_sexp codec v)
907907- | _ -> value_error ("expected single argument for " ^ tag));
908908- project =
909909- (fun v ->
910910- match project v with
911911- | Some x -> Some [ to_sexp codec x ]
912912- | None -> None);
913913- }
914914-915915- let casev tag (codec : 'b t) inject project =
916916- {
917917- tag;
918918- inject =
919919- (fun args ->
920920- match of_sexp codec (Sexp.List args) with
921921- | Ok v -> Ok (inject v)
922922- | Error e -> Error e);
923923- project =
924924- (fun v ->
925925- match project v with
926926- | Some x -> (
927927- match to_sexp codec x with
928928- | Sexp.List l -> Some l
929929- | Sexp.Atom _ as a -> Some [ a ])
930930- | None -> None);
931931- }
914914+ let case0 tag value project = Vcase0 { tag; value; project }
915915+ let case1 tag codec inject project = Vcase1 { tag; codec; inject; project }
916916+ let casev tag codec inject project = Vcase1 { tag; codec; inject; project }
932917933918 let variant ?kind ?doc cases =
934919 Variant
···1305129013061291(* ---- Stream-based GADT decoder ---- *)
1307129213081308-(* Reconstruct a Sexp.t from the token stream (used by Value, Variant, etc.) *)
12931293+(* Skip one S-expression from the token stream without building Sexp.t *)
12941294+let rec skip_sexp_stream s =
12951295+ match P.next s with
12961296+ | Error e -> Error (Parse_error e)
12971297+ | Ok P.Eof -> Error (Parse_error "unexpected end of input")
12981298+ | Ok (P.Atom _) -> Ok ()
12991299+ | Ok P.Close -> Error (Parse_error "unexpected ')'")
13001300+ | Ok P.Open ->
13011301+ let rec skip_rest () =
13021302+ match P.peek s with
13031303+ | Error e -> Error (Parse_error e)
13041304+ | Ok P.Close ->
13051305+ let _ = P.next s in
13061306+ Ok ()
13071307+ | Ok P.Eof -> Error (Parse_error "unexpected end of input in list")
13081308+ | Ok _ -> (
13091309+ match skip_sexp_stream s with
13101310+ | Ok () -> skip_rest ()
13111311+ | Error e -> Error e)
13121312+ in
13131313+ skip_rest ()
13141314+13151315+(* Skip tokens until balanced Close (caller has already consumed Open) *)
13161316+let skip_until_close s =
13171317+ let rec loop () =
13181318+ match P.peek s with
13191319+ | Error e -> Error (Parse_error e)
13201320+ | Ok P.Close ->
13211321+ let _ = P.next s in
13221322+ Ok ()
13231323+ | Ok P.Eof -> Error (Parse_error "unexpected end of input, expected ')'")
13241324+ | Ok _ -> (
13251325+ match skip_sexp_stream s with Ok () -> loop () | Error e -> Error e)
13261326+ in
13271327+ loop ()
13281328+13291329+(* Reconstruct a Sexp.t from the token stream (used by Value only) *)
13091330let rec sexp_of_stream s =
13101331 match P.next s with
13111332 | Error e -> Error (Parse_error e)
···13821403 | Ok _ -> (
13831404 if m.dec_skip i builder then begin
13841405 (* Skip this element by consuming the token(s) *)
13851385- match sexp_of_stream s with
13861386- | Ok _ -> loop builder (i + 1)
14061406+ match skip_sexp_stream s with
14071407+ | Ok () -> loop builder (i + 1)
13871408 | Error e -> Error e
13881409 end
13891410 else
···14111432 | None -> dict)
14121433 m.mem_decs Dict.empty
14131434 in
14141414- (* Collect values inside a member pair until Close *)
14151415- let collect_values () =
14161416- let rec aux acc =
14171417- match P.peek s with
14181418- | Error e -> Error (Parse_error e)
14191419- | Ok P.Close ->
14201420- let _ = P.next s in
14211421- Ok (List.rev acc)
14221422- | Ok P.Eof ->
14231423- Error (Parse_error "unexpected end of input in member pair")
14241424- | Ok _ -> (
14251425- match sexp_of_stream s with
14261426- | Ok v -> aux (v :: acc)
14271427- | Error e -> Error e)
14281428- in
14291429- aux []
14301430- in
14311431- (* Convert collected values to a single Sexp.t, matching parse_alist:
14321432- (key value) -> value
14331433- (key v1 v2 ...) -> List [v1; v2; ...] *)
14341434- let values_to_sexp = function [ v ] -> v | vs -> Sexp.List vs in
14351435 let rec loop dict =
14361436 match P.peek s with
14371437 | Error e -> Error (Parse_error e)
···14621462 match String_map.find_opt name m.mem_decs with
14631463 | None -> (
14641464 (* Unknown member, skip remaining values until Close *)
14651465- match collect_values () with
14651465+ match skip_until_close s with
14661466 | Error e -> Error e
14671467- | Ok _ -> loop dict)
14671467+ | Ok () -> loop dict)
14681468 | Some (Mem_dec md) -> (
14691469- match collect_values () with
14701470- | Error e -> Error e
14711471- | Ok [] -> loop dict
14721472- | Ok values -> (
14731473- let sexp_val = values_to_sexp values in
14741474- match of_sexp md.codec sexp_val with
14751475- | Ok v -> loop (Dict.add md.id v dict)
14761476- | Error e -> Error e))))
14691469+ (* Check if there is a value *)
14701470+ match P.peek s with
14711471+ | Error e -> Error (Parse_error e)
14721472+ | Ok P.Close ->
14731473+ (* Empty member (key) -- skip *)
14741474+ let _ = P.next s in
14751475+ loop dict
14761476+ | Ok _ -> (
14771477+ (* Decode the value directly from the stream *)
14781478+ match decode_stream md.codec s with
14791479+ | Error e -> Error e
14801480+ | Ok v -> (
14811481+ (* Skip any remaining values and consume Close *)
14821482+ match skip_until_close s with
14831483+ | Error e -> Error e
14841484+ | Ok () -> loop (Dict.add md.id v dict))))))
14771485 | Ok P.Open | Ok P.Close | Ok P.Eof ->
14781486 Error
14791487 (Parse_error "expected member name (atom) in association list")
···15211529and decode_variant_stream : type a.
15221530 a vcase list -> P.stream -> (a, codec_error) result =
15231531 fun cases s ->
15241524- let case_map = List.map (fun c -> (c.tag, c)) cases in
15321532+ let case_map = List.map (fun c -> (vcase_tag c, c)) cases in
15251533 match P.peek s with
15261534 | Error e -> Error (Parse_error e)
15271535 | Ok (P.Atom _) -> (
15281536 match P.next s with
15291537 | Ok (P.Atom tag) -> (
15301538 match List.assoc_opt tag case_map with
15311531- | Some case -> case.inject []
15391539+ | Some (Vcase0 c) -> Ok c.value
15401540+ | Some (Vcase1 _) ->
15411541+ value_error ("expected arguments for variant " ^ tag)
15321542 | None -> value_error ("unknown variant tag: " ^ tag))
15331543 | _ -> assert false)
15341544 | Ok P.Open -> (
···15401550 | Ok (P.Atom tag) -> (
15411551 match List.assoc_opt tag case_map with
15421552 | None -> value_error ("unknown variant tag: " ^ tag)
15431543- | Some case -> (
15441544- (* Read remaining args as Sexp.t list *)
15451545- let rec collect_args acc =
15461546- match P.peek s with
15471547- | Error e -> Error (Parse_error e)
15481548- | Ok P.Close ->
15491549- let _ = P.next s in
15501550- Ok (List.rev acc)
15511551- | Ok _ -> (
15521552- match sexp_of_stream s with
15531553- | Ok v -> collect_args (v :: acc)
15541554- | Error e -> Error e)
15551555- in
15561556- match collect_args [] with
15531553+ | Some (Vcase0 c) -> (
15541554+ (* Nullary case: skip any content, consume Close *)
15551555+ match skip_until_close s with
15561556+ | Error e -> Error e
15571557+ | Ok () -> Ok c.value)
15581558+ | Some (Vcase1 c) -> (
15591559+ (* Decode the payload directly from the stream *)
15601560+ match decode_stream c.codec s with
15571561 | Error e -> Error e
15581558- | Ok args -> case.inject args))
15621562+ | Ok v -> (
15631563+ (* Skip any remaining values and consume Close *)
15641564+ match skip_until_close s with
15651565+ | Error e -> Error e
15661566+ | Ok () -> Ok (c.inject v))))
15591567 | Ok _ -> Error (Parse_error "expected variant tag (atom)"))
15601568 | Ok P.Eof -> Error (Parse_error "unexpected end of input, expected variant")
15611569 | Ok P.Close -> Error (Parse_error "unexpected ')' where variant expected")