• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Kakadu / zanuda / 27

18 Sep 2025 07:20AM UTC coverage: 86.255% (+0.01%) from 86.245%
27

push

github

Kakadu
Enable alpine and opensuse back

Signed-off-by: Kakadu <Kakadu@pm.me>

2209 of 2561 relevant lines covered (86.26%)

523.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.75
/src/pattern/Tast_pattern.ml
1
[@@@ocaml.text "/*"]
2

3
(** Copyright 2021-2025, Kakadu. *)
4

5
(** SPDX-License-Identifier: LGPL-3.0-or-later *)
6

7
[@@@ocaml.text "/*"]
8

9
module Ast_pattern0 = struct
10
  exception Expected of Location.t * string
11

12
  let fail loc expected = raise (Expected (loc, expected))
70,085✔
13

14
  type context =
15
    { (* [matched] counts how many constructors have been matched. This is used to find what
16
         pattern matches the most some piece of ast in [Ast_pattern.alt]. In the case where
17
         all branches fail to match, we report the error from the one that matches the
18
         most.
19
         This is only incremented by combinators that can fail. *)
20
      mutable matched : int
21
    }
22

23
  type ('matched_value, 'k, 'k_result) t =
24
    | T of (context -> Location.t -> 'matched_value -> 'k -> 'k_result)
25

26
  (* end of copy-paste from https://github.com/ocaml-ppx/ppxlib/blob/0.22.2/src/ast_pattern0.ml *)
27
  (* TODO: deal with licencing issues *)
28
end
29

30
open Location
31
open Format
32
open Ast_pattern0
33

34
let debug_enabled = false
35

36
let log fmt =
37
  let open Format in
1,198✔
38
  if debug_enabled then kasprintf (printf "%s\n%!") fmt else ifprintf std_formatter fmt
×
39
;;
40

41
type ('a, 'b, 'c) t = ('a, 'b, 'c) Ast_pattern0.t
42

43
let save_context ctx = ctx.matched
123,129✔
44
let restore_context ctx backup = ctx.matched <- backup
80,964✔
45
let incr_matched c = c.matched <- c.matched + 1
4,057✔
46

47
let parse (T f) loc ?on_error x k =
48
  try f { matched = 0 } loc x k with
1,598✔
49
  | Expected (loc, expected) ->
29,064✔
50
    (match on_error with
51
     | None -> Location.raise_errorf ~loc "%s expected" expected
×
52
     | Some f -> f expected)
29,064✔
53
;;
54

55
module Packed = struct
56
  type ('a, 'b) t = T : ('a, 'b, 'c) Ast_pattern0.t * 'b -> ('a, 'c) t
57

58
  let create t f = T (t, f)
×
59
  let parse (T (t, f)) loc x = parse t loc x f
×
60
end
61

62
let __ : 'a 'b. ('a, 'a -> 'b, 'b) t =
63
  T
64
    (fun ctx _loc x k ->
65
      incr_matched ctx;
2,786✔
66
      k x)
2,786✔
67
;;
68

69
let as__ : 'a 'b 'c. ('a, 'b, 'c) t -> ('a, 'a -> 'b, 'c) t =
70
  fun (T f1) ->
71
  T
3,606✔
72
    (fun ctx loc x k ->
73
      let k = f1 ctx loc x (k x) in
259✔
74
      k)
120✔
75
;;
76

77
let pair (T f1) (T f2) =
78
  T
9,378✔
79
    (fun ctx loc (x1, x2) k ->
80
      let k = f1 ctx loc x1 k in
408✔
81
      let k = f2 ctx loc x2 k in
389✔
82
      k)
260✔
83
;;
84

85
let ( ** ) = pair
86

87
let __' =
88
  T
89
    (fun ctx loc x k ->
90
      incr_matched ctx;
21✔
91
      k { loc; txt = x })
21✔
92
;;
93

94
let drop : 'a 'b. ('a, 'b, 'b) t =
95
  T
96
    (fun ctx _loc _ k ->
97
      incr_matched ctx;
1,199✔
98
      k)
1,199✔
99
;;
100

101
let cst ~to_string ?(equal = Stdlib.( = )) v =
5,399✔
102
  T
5,399✔
103
    (fun ctx loc x k ->
104
      if equal x v
135✔
105
      then (
51✔
106
        incr_matched ctx;
107
        (* printf "cst succeeded for %s\n%!" (to_string v); *)
108
        k)
51✔
109
      else fail loc (to_string v))
84✔
110
;;
111

112
let int v = cst ~to_string:Int.to_string v
470✔
113
let char v = cst ~to_string:(Printf.sprintf "%C") v
×
114
let string v = cst ~to_string:(Printf.sprintf "%S") v
4,929✔
115
let float v = cst ~to_string:Float.to_string v
×
116
let int32 v = cst ~to_string:Int32.to_string v
×
117
let int64 v = cst ~to_string:Int64.to_string v
×
118
let nativeint v = cst ~to_string:Nativeint.to_string v
×
119
let bool v = cst ~to_string:Bool.to_string v
×
120

121
let false_ =
122
  T
123
    (fun ctx loc x k ->
124
      match x with
×
125
      | false ->
×
126
        ctx.matched <- ctx.matched + 1;
127
        k
128
      | _ -> fail loc "false")
×
129
;;
130

131
let true_ =
132
  T
133
    (fun ctx loc x k ->
134
      match x with
×
135
      | true ->
×
136
        ctx.matched <- ctx.matched + 1;
137
        k
138
      | _ -> fail loc "true")
×
139
;;
140

141
let nil =
142
  T
143
    (fun ctx loc x k ->
144
      log "trying [] \n%!";
402✔
145
      match x with
402✔
146
      | [] ->
315✔
147
        ctx.matched <- ctx.matched + 1;
148
        k
149
      | _ -> fail loc "[]")
87✔
150
;;
151

152
let ( ^:: ) (T f0) (T f1) =
153
  T
22,472✔
154
    (fun ctx loc x k ->
155
      match x with
651✔
156
      | x0 :: x1 ->
600✔
157
        ctx.matched <- ctx.matched + 1;
158
        (* Format.printf "trying elem of cons cell\n%!"; *)
159
        let k = f0 ctx loc x0 k in
160
        (* Format.printf "trying tail of cons cell\n%!"; *)
161
        let k = f1 ctx loc x1 k in
498✔
162
        (* Format.printf "trying  cons cell succeeded\n%!"; *)
163
        k
359✔
164
      | _ ->
51✔
165
        (* Format.printf "failing elem of cons cell\n%!"; *)
166
        fail loc "::")
167
;;
168

169
let list (T fel) =
170
  let rec helper acc ctx loc xs k =
2✔
171
    match xs with
6✔
172
    | [] -> k (List.rev acc)
2✔
173
    | h :: tl ->
4✔
174
      (match fel ctx loc h Fun.id with
175
       | x -> helper (x :: acc) ctx loc tl k)
4✔
176
  in
177
  T (fun ctx loc xs k -> helper [] ctx loc xs k)
2✔
178
;;
179

180
let none =
181
  T
182
    (fun ctx loc x k ->
183
      match x with
72✔
184
      | None ->
72✔
185
        ctx.matched <- ctx.matched + 1;
186
        k
187
      | _ -> fail loc "None")
×
188
;;
189

190
let some (T f0) =
191
  T
8,186✔
192
    (fun ctx loc x k ->
193
      match x with
156✔
194
      | Some x0 ->
156✔
195
        ctx.matched <- ctx.matched + 1;
196
        let k = f0 ctx loc x0 k in
197
        k
73✔
198
      | _ -> fail loc "Some")
×
199
;;
200

201
let triple (T f1) (T f2) (T f3) =
202
  T
×
203
    (fun ctx loc (x1, x2, x3) k ->
204
      let k = f1 ctx loc x1 k in
×
205
      let k = f2 ctx loc x2 k in
×
206
      let k = f3 ctx loc x3 k in
×
207
      k)
×
208
;;
209

210
let alt (T f1) (T f2) =
211
  T
12,338✔
212
    (fun ctx loc x k ->
213
      let backup = save_context ctx in
41,838✔
214
      try f1 ctx loc x k with
817✔
215
      | e1 ->
41,021✔
216
        let m1 = save_context ctx in
217
        restore_context ctx backup;
41,021✔
218
        (try f2 ctx loc x k with
751✔
219
         | e2 ->
40,270✔
220
           let m2 = save_context ctx in
221
           if m1 >= m2
40,270✔
222
           then (
39,943✔
223
             restore_context ctx m1;
224
             raise e1)
39,943✔
225
           else raise e2))
327✔
226
;;
227

228
let ( ||| ) = alt
229
let map (T func) ~f = T (fun ctx loc x k -> func ctx loc x (f k))
×
230
let map' (T func) ~f = T (fun ctx loc x k -> func ctx loc x (f loc k))
×
231
let map_result (T func) ~f = T (fun ctx loc x k -> f (func ctx loc x k))
×
232
let ( >>| ) t f = map t ~f
×
233
let map0 (T func) ~f = T (fun ctx loc x k -> func ctx loc x (k f))
330✔
234
let map1 (T func) ~f = T (fun ctx loc x k -> func ctx loc x (fun a -> k (f a)))
604✔
235
let map2 (T func) ~f = T (fun ctx loc x k -> func ctx loc x (fun a b -> k (f a b)))
367✔
236
let map3 (T func) ~f = T (fun ctx loc x k -> func ctx loc x (fun a b c -> k (f a b c)))
57✔
237

238
let map4 (T func) ~f =
239
  T (fun ctx loc x k -> func ctx loc x (fun a b c d -> k (f a b c d)))
9✔
240
;;
241

242
let map5 (T func) ~f =
243
  T (fun ctx loc x k -> func ctx loc x (fun a b c d e -> k (f a b c d e)))
22✔
244
;;
245

246
let map6 (T func) ~f:fmap =
247
  T (fun ctx loc x k -> func ctx loc x (fun a b c d e f -> k (fmap a b c d e f)))
1✔
248
;;
249

250
let map7 (T func) ~f:fmap =
251
  T (fun ctx loc x k -> func ctx loc x (fun a b c d e f g -> k (fmap a b c d e f g)))
3✔
252
;;
253

254
let map0' (T func) ~f = T (fun ctx loc x k -> func ctx loc x (k (f loc)))
×
255
let map1' (T func) ~f = T (fun ctx loc x k -> func ctx loc x (fun a -> k (f loc a)))
×
256
let map2' (T func) ~f = T (fun ctx loc x k -> func ctx loc x (fun a b -> k (f loc a b)))
×
257
let map_result (T func) ~f = T (fun ctx loc x k -> f (func ctx loc x k))
×
258
let alt_option some none = alt (map1 some ~f:(fun x -> Some x)) (map0 none ~f:None)
×
259

260
let many (T f) =
261
  T (fun ctx loc l k -> k (ListLabels.map l ~f:(fun x -> f ctx loc x (fun x -> x))))
×
262
;;
263

264
let loc (T f) = T (fun ctx _loc (x : _ Ppxlib.Loc.t) k -> f ctx x.loc x.txt k)
×
265
let pack0 t = map t ~f:(fun f -> f ())
×
266
let pack2 t = map t ~f:(fun f x y -> f (x, y))
×
267
let pack3 t = map t ~f:(fun f x y z -> f (x, y, z))
×
268

269
(* end of copy-paste from https://github.com/ocaml-ppx/ppxlib/blob/0.22.2/src/ast_pattern.ml *)
270
(* TODO: deal with licencing issues *)
271

272
let lident (T fident) =
273
  T
584✔
274
    (fun ctx loc x k ->
275
      match x with
136✔
276
      | Longident.Lident id ->
126✔
277
        ctx.matched <- ctx.matched + 1;
278
        k |> fident ctx loc id
279
      | _ -> fail loc "lident")
10✔
280
;;
281

282
let elongident (lident : Longident.t) =
283
  T
56✔
284
    (fun ctx loc x k ->
285
      if Stdlib.compare x lident = 0
183✔
286
      then (
15✔
287
        ctx.matched <- ctx.matched + 1;
288
        k)
289
      else fail loc "elongident")
168✔
290
;;
291

292
let path_pident (T fident) =
293
  T
×
294
    (fun ctx loc x k ->
295
      match x with
×
296
      | Path.Pident id ->
×
297
        ctx.matched <- ctx.matched + 1;
298
        k |> fident ctx loc id
299
      | _ -> fail loc (sprintf "path_pident"))
×
300
;;
301

302
let path xs =
303
  let rec helper ps ctx loc x k =
5,657✔
304
    let cmp_names l r =
13,444✔
305
      let ans = String.equal l r in
7,060✔
306
      (* printf "\t\tCompare names %s and %s:  %b\n%!" l r ans; *)
307
      ans
7,060✔
308
    in
309
    match x, ps with
310
    | Path.Pident id, [ id0 ] ->
117✔
311
      if cmp_names (Ident.name id) id0
117✔
312
      then (
110✔
313
        let () = ctx.matched <- ctx.matched + 1 in
314
        k)
315
      else fail loc "path"
7✔
316
    | Path.Pdot (next, id), id0 :: ids when cmp_names id id0 -> helper ids ctx loc next k
152✔
317
    | Path.Papply _, _ -> fail loc "path got Papply"
×
318
    | _ -> fail loc (sprintf "path %s" (String.concat "." xs))
13,175✔
319
  in
320
  T (helper (List.rev xs))
5,657✔
321
;;
322

323
let path_of_list = function
324
  | [] -> failwith "Bad argument: path_of_list"
×
325
  | s :: tl ->
2✔
326
    ListLabels.fold_left
327
      tl
328
      ~init:(Path.Pident (Ident.create_local s))
2✔
329
      ~f:(fun acc x -> Path.Pdot (acc, x))
4✔
330
;;
331

332
let%test_module " " =
333
  (module struct
334
    let names = [ "Stdlib!"; "List"; "length" ]
335

336
    [%%if ocaml_version < (5, 0, 0)]
337

338
    let pp_path = Path.print
339

340
    [%%else]
341

342
    let pp_path = Format_doc.compat Path.print
343

344
    [%%endif]
345

346
    let%test_unit _ =
347
      let old = !Clflags.unique_ids in
348
      Clflags.unique_ids := false;
349
      [%test_eq: Base.string]
1✔
350
        "Stdlib!.List.length"
351
        (asprintf "%a" pp_path (path_of_list names));
1✔
352
      Clflags.unique_ids := old
1✔
353
    ;;
354

355
    let%test _ =
356
      let noloc =
1✔
357
        Warnings.
358
          { loc_start = Lexing.dummy_pos; loc_end = Lexing.dummy_pos; loc_ghost = true }
359
      in
360
      parse (path names) noloc ~on_error:(fun _ -> false) (path_of_list names) true
×
361
    ;;
362
  end)
363
;;
364

365
open Typedtree
366

367
let econst (T f0) =
368
  T
×
369
    (fun ctx loc x k ->
370
      match x.exp_desc with
×
371
      | Texp_constant n ->
×
372
        ctx.matched <- ctx.matched + 1;
373
        f0 ctx loc n k
374
      | _ -> fail loc (sprintf "econst"))
×
375
;;
376

377
let eint (T f0) =
378
  T
470✔
379
    (fun ctx loc x k ->
380
      match x.exp_desc with
17✔
381
      | Texp_constant (Asttypes.Const_int n) ->
8✔
382
        ctx.matched <- ctx.matched + 1;
383
        f0 ctx loc n k
384
      | _ -> fail loc "eint")
9✔
385
;;
386

387
let estring =
388
  T
389
    (fun ctx loc x k ->
390
      match x.exp_desc with
15✔
391
      | Texp_constant (Asttypes.Const_string (s, _, None)) ->
15✔
392
        ctx.matched <- ctx.matched + 1;
393
        k s
394
      | _ -> fail loc "estring")
×
395
;;
396

397
let ebool =
398
  T
399
    (fun ctx loc x k ->
400
      match x.exp_desc with
95✔
401
      | Texp_construct ({ txt = Lident "true" }, _, []) ->
11✔
402
        ctx.matched <- ctx.matched + 1;
403
        k true
404
      | Texp_construct ({ txt = Lident "false" }, _, []) ->
8✔
405
        ctx.matched <- ctx.matched + 1;
406
        k false
407
      | _ -> fail loc (sprintf "ebool"))
76✔
408
;;
409

410
[%%if ocaml_version < (5, 0, 0)]
411

412
let tpat_var (T fname) =
413
  T
1,519✔
414
    (fun ctx loc x k ->
415
      match x.pat_desc with
51✔
416
      | Tpat_var (_, { txt }) ->
51✔
417
        ctx.matched <- ctx.matched + 1;
418
        k |> fname ctx loc txt
419
      | _ -> fail loc "tpat_var")
×
420
;;
421

422
let tpat_id (T fname) =
423
  T
1,888✔
424
    (fun ctx loc x k ->
425
      match x.pat_desc with
291✔
426
      | Tpat_var (id, { loc }) ->
231✔
427
        ctx.matched <- ctx.matched + 1;
428
        k |> fname ctx loc id
429
      | _ -> fail loc "tpat_var_id")
60✔
430
;;
431

432
[%%else]
433

434
let tpat_var (T fname) =
435
  T
436
    (fun (type kind) ctx loc (x : kind pattern_desc pattern_data) k ->
437
      match x.pat_desc with
438
      | Tpat_var (_, { txt }, _uid) ->
439
        ctx.matched <- ctx.matched + 1;
440
        k |> fname ctx loc txt
441
      | Tpat_value v ->
442
        (match (v :> pattern).pat_desc with
443
         | Tpat_var (_, { txt }, _uid) ->
444
           ctx.matched <- ctx.matched + 1;
445
           k |> fname ctx loc txt
446
         | _ -> fail loc "tpat_var")
447
      | _ -> fail loc "tpat_var")
448
;;
449

450
let tpat_id (T fname) =
451
  T
452
    (fun (type kind) ctx loc (x : kind pattern_desc pattern_data) k ->
453
      match x.pat_desc with
454
      | Typedtree.Tpat_value v ->
455
        (match (v :> pattern).pat_desc with
456
         | Tpat_var (id, { loc }, _uid) ->
457
           ctx.matched <- ctx.matched + 1;
458
           k |> fname ctx loc id
459
         | _ -> fail loc "tpat_id")
460
      | Tpat_var (id, { loc }, _uid) ->
461
        ctx.matched <- ctx.matched + 1;
462
        k |> fname ctx loc id
463
      | _ -> fail loc "tpat_id 2")
464
;;
465

466
[%%endif]
467

468
let tpat_constructor (T fname) (T fargs) =
469
  T
5,796✔
470
    (fun ctx loc x k ->
471
      match x.pat_desc with
84✔
472
      | Tpat_construct ({ txt }, _, args, _) ->
75✔
473
        ctx.matched <- ctx.matched + 1;
474
        k |> fname ctx loc txt |> fargs ctx loc args
66✔
475
      | _ -> fail loc "tpat_constructor")
9✔
476
;;
477

478
let tpat_tuple (T fargs) =
479
  T
×
480
    (fun ctx loc x k ->
481
      match x.pat_desc with
×
482
      | Tpat_tuple pats ->
×
483
        ctx.matched <- ctx.matched + 1;
484
        k |> fargs ctx loc pats
485
      | _ -> fail loc "tpat_tuple")
×
486
;;
487

488
let tpat_value (T fpat) =
489
  T
×
490
    (fun ctx loc x k ->
491
      match x.pat_desc with
×
492
      | Tpat_value arg ->
×
493
        let inner = (arg :> value pattern_desc pattern_data) in
494
        ctx.matched <- ctx.matched + 1;
495
        k |> fpat ctx loc inner
496
      | _ -> fail loc "tpat_value")
×
497
;;
498

499
let tpat_exception (T fpat) =
500
  T
×
501
    (fun ctx loc x k ->
502
      match x.pat_desc with
×
503
      | Tpat_exception exc ->
×
504
        ctx.matched <- ctx.matched + 1;
505
        k |> fpat ctx loc exc
506
      | _ -> fail loc "tpat_exception")
×
507
;;
508

509
let tpat_any =
510
  T
511
    (fun ctx loc x k ->
512
      match x.pat_desc with
3✔
513
      | Tpat_any ->
2✔
514
        ctx.matched <- ctx.matched + 1;
515
        k
516
      | _ -> fail loc "tpat_any")
1✔
517
;;
518

519
let texp_ident (T fpath) =
520
  T
14,703✔
521
    (fun ctx loc x k ->
522
      let __ _ = log "texp_ident %a\n%!" My_printtyped.expr x in
×
523
      match x.exp_desc with
524
      | Texp_ident (path, _, _) ->
11,725✔
525
        ctx.matched <- ctx.matched + 1;
526
        let ans = fpath ctx loc path k in
527
        log "texp_ident + %a\n%!" My_printtyped.expr x;
796✔
528
        ans
796✔
529
      | _ -> fail loc "texp_ident")
3,123✔
530
;;
531

532
let texp_ident_loc (T fpath) =
533
  T
1,247✔
534
    (fun ctx loc x k ->
535
      match x.exp_desc with
14✔
536
      | Texp_ident (path, _, _) ->
10✔
537
        ctx.matched <- ctx.matched + 1;
538
        k x.exp_loc |> fpath ctx loc path
10✔
539
      | _ -> fail loc "texp_ident")
4✔
540
;;
541

542
(* TODO(Kakadu): accept and Ident, and not a string *)
543
let pident (T fstr) =
544
  T
7,439✔
545
    (fun ctx loc x k ->
546
      match x with
211✔
547
      | Path.Pident id -> fstr ctx loc (Ident.name id) k
93✔
548
      | _ -> fail loc "pident")
118✔
549
;;
550

551
let texp_ident_typ (T fpath) (T ftyp) =
552
  T
279✔
553
    (fun ctx loc x k ->
554
      (* let __ _ = Format.printf "texp_ident_typ %a\n%!" MyPrinttyped.expr x in *)
555
      match x.exp_desc with
1,597✔
556
      | Texp_ident (path, _, typ) ->
829✔
557
        ctx.matched <- ctx.matched + 1;
558
        k |> fpath ctx loc path |> ftyp ctx loc typ.Types.val_type
437✔
559
      | _ -> fail loc "texp_ident_typ")
768✔
560
;;
561

562
[%%if ocaml_version < (5, 0, 0)]
563

564
let texp_assert (T fexp) =
565
  T
56✔
566
    (fun ctx loc x k ->
567
      match x.exp_desc with
1,457✔
568
      | Texp_assert e ->
1✔
569
        ctx.matched <- ctx.matched + 1;
570
        fexp ctx loc e k
571
      | _ -> fail loc "texp_assert")
1,456✔
572
;;
573

574
[%%else]
575

576
let texp_assert (T fexp) =
577
  T
578
    (fun ctx loc x k ->
579
       match x.exp_desc with
580
       | Texp_assert (e, _) ->
581
         ctx.matched <- ctx.matched + 1;
582
         fexp ctx loc e k
583
       | _ -> fail loc "texp_assert"
584
     : context -> Warnings.loc -> expression -> 'a -> 'b)
585
;;
586

587
[%%endif]
588

589
let texp_apply (T f0) (T args0) =
590
  T
5,963✔
591
    (fun ctx loc x k ->
592
      (* let __ _ = log "texp_apply %a\n%!" MyPrinttyped.expr x in *)
593
      match x.exp_desc with
25,683✔
594
      | Texp_apply (f, args) ->
3,584✔
595
        ctx.matched <- ctx.matched + 1;
596
        let ans = k |> f0 ctx loc f |> args0 ctx loc args in
73✔
597
        (* let _ = log "texp_apply + %a\n%!" MyPrinttyped.expr x in *)
598
        ans
43✔
599
      | _ -> fail loc "texp_apply")
22,099✔
600
;;
601

602
let texp_apply_nolabelled (T f0) (T args0) =
603
  let exception EarlyExit in
1,538✔
604
  T
605
    (fun ctx loc x k ->
606
      match x.exp_desc with
3,082✔
607
      | Texp_apply (f, args) ->
482✔
608
        ctx.matched <- ctx.matched + 1;
609
        let k = f0 ctx loc f k in
610
        (try
95✔
611
           let args =
612
             ListLabels.map args ~f:(function
613
               | Asttypes.Labelled _, _ | Asttypes.Optional _, _ | _, None ->
×
614
                 raise EarlyExit
615
               | _, Some x -> x)
206✔
616
           in
617
           args0 ctx loc args k
22✔
618
         with
619
         | EarlyExit -> fail loc "texp_apply: None among the arguments ")
2✔
620
      | _ -> fail loc "texp_apply")
2,600✔
621
;;
622

623
let texp_construct (T fpath) (T fcd) (T fargs) =
624
  T
216✔
625
    (fun ctx loc x k ->
626
      match x.exp_desc with
1,481✔
627
      | Texp_construct (path, cd, args) ->
192✔
628
        ctx.matched <- ctx.matched + 1;
629
        let k = fpath ctx loc path.txt k in
630
        k |> fcd ctx loc cd |> fargs ctx loc args
24✔
631
      | _ -> fail loc (sprintf "texp_construct"))
1,289✔
632
;;
633

634
let texp_assert_false () = texp_assert (texp_construct (lident (string "false")) drop nil)
56✔
635

636
let texp_let (T fvbs) (T fexpr) =
637
  T
1,469✔
638
    (fun ctx loc x k ->
639
      match x.exp_desc with
1,267✔
640
      | Texp_let (_flg, vbs, expr) ->
18✔
641
        ctx.matched <- ctx.matched + 1;
642
        k |> fvbs ctx loc vbs |> fexpr ctx loc expr
15✔
643
      | _ -> fail loc (sprintf "texp_let"))
1,249✔
644
;;
645

646
let nolabel =
647
  T
648
    (fun ctx loc x k ->
649
      match x with
253✔
650
      | Asttypes.Nolabel ->
234✔
651
        ctx.matched <- ctx.matched + 1;
652
        k
653
      | _ -> fail loc "nolabel")
19✔
654
;;
655

656
let labelled (T fstr) =
657
  T
56✔
658
    (fun ctx loc x k ->
659
      match x with
2✔
660
      | Asttypes.Labelled s ->
2✔
661
        ctx.matched <- ctx.matched + 1;
662
        k |> fstr ctx loc s
663
      | _ -> fail loc "labelled")
×
664
;;
665

666
let texp_apply1 f x = texp_apply f ((nolabel ** some x) ^:: nil)
3,404✔
667
let texp_apply2 f x y = texp_apply f ((nolabel ** some x) ^:: (nolabel ** some y) ^:: nil)
2,167✔
668

669
[%%if ocaml_version < (4, 11, 2)]
670

671
(* 4.10 *)
672
type case_val = Typedtree.case
673
type case_comp = Typedtree.case
674
type value_pat = pattern
675
type comp_pat = pattern
676

677
[%%else]
678

679
type case_val = value case
680
type case_comp = computation case
681
type value_pat = value pattern_desc pattern_data
682
type comp_pat = computation pattern_desc pattern_data
683

684
[%%endif]
685
[%%if ocaml_version < (5, 0, 0)]
686

687
let texp_function (T fcases) =
688
  T
×
689
    (fun ctx loc e k ->
690
      match e.exp_desc with
×
691
      | Texp_function { cases } ->
×
692
        ctx.matched <- ctx.matched + 1;
693
        k |> fcases ctx loc cases
694
      | _ -> fail loc "texp_function")
×
695
;;
696

697
let texp_function_body (T fargs) (T frhs) =
698
  let rec helper acc ctx loc e k =
6,786✔
699
    match e.exp_desc with
6,265✔
700
    | Texp_function
944✔
701
        { cases =
702
            [ { c_lhs = { pat_desc = Tpat_var (pid, _); pat_loc; _ }
703
              ; c_rhs
704
              ; c_guard = None
705
              }
706
            ]
707
        ; arg_label
708
        ; partial = Total
709
        } -> helper ((arg_label, (pid, pat_loc)) :: acc) ctx loc c_rhs k
710
    | _ when [] = acc -> fail loc "texp_function_body"
4,631✔
711
    | _ -> k |> fargs ctx loc (List.rev acc) |> frhs ctx loc e
603✔
712
  in
713
  T (helper [])
6,786✔
714
;;
715

716
let texp_function_cases
717
  :  ((Asttypes.arg_label * (Ident.t * Location.t)) list, 'a, 'b) t
718
  -> (value case list, 'b, 'c) t
719
  -> (expression, 'a, 'c) t
720
  =
721
  fun (T fargs) (T frhs) ->
722
  let rec helper acc ctx loc e k =
5,818✔
723
    match e.exp_desc with
4,730✔
724
    | Typedtree.Texp_function
560✔
725
        { cases =
726
            [ { c_lhs = { pat_desc = Tpat_var (pid, tag); _ }; c_rhs; c_guard = _ } ]
727
        ; arg_label
728
        ; partial = Total
729
        } -> helper ((arg_label, (pid, tag.loc)) :: acc) ctx loc c_rhs k
730
    | Texp_function { cases = _ :: _ :: _ as cases; _ } ->
91✔
731
      k |> fargs ctx loc (List.rev acc) |> frhs ctx loc cases
62✔
732
    | _ -> fail loc "texp_function_cases"
4,079✔
733
  in
734
  T (helper [])
5,818✔
735
;;
736

737
[%%else]
738

739
let texp_function_cases (T fparam) (T fcases) =
740
  T
741
    (fun ctx loc e k ->
742
      match e.exp_desc with
743
      | Texp_function (params, Tfunction_cases cases) ->
744
        ctx.matched <- ctx.matched + 1;
745
        k
746
        |> fparam
747
             ctx
748
             loc
749
             (List.map (fun p -> p.Typedtree.fp_arg_label, (p.fp_param, p.fp_loc)) params)
750
        |> fcases ctx loc cases.cases
751
      | _ -> fail loc "texp_function")
752
;;
753

754
let texp_function_body (T fparam) (T fcases) =
755
  T
756
    (fun ctx loc e k ->
757
      match e.exp_desc with
758
      | Typedtree.Texp_function (params, Tfunction_body e) ->
759
        ctx.matched <- ctx.matched + 1;
760
        k
761
        |> fparam
762
             ctx
763
             loc
764
             (List.map (fun p -> p.fp_arg_label, (p.fp_param, p.fp_loc)) params)
765
        |> fcases ctx loc e
766
      | _ -> fail loc "texp_function")
767
;;
768

769
[%%endif]
770

771
let case (T pat) (T guard) (T rhs) =
772
  T
7,319✔
773
    (fun ctx loc { c_lhs; c_rhs; c_guard } k ->
774
      k |> pat ctx loc c_lhs |> guard ctx loc c_guard |> rhs ctx loc c_rhs)
53✔
775
;;
776

777
let ccase (T pat) (T guard) (T rhs) =
778
  T
×
779
    (fun ctx loc { c_lhs; c_rhs; c_guard } k ->
780
      k |> pat ctx loc c_lhs |> guard ctx loc c_guard |> rhs ctx loc c_rhs)
×
781
;;
782

783
[%%if ocaml_version < (5, 0, 0)]
784

785
let texp_match (T fexpr) (T fcomp_cases) (T fval_cases) =
786
  let rec split (type _a) (comps, vals) (cases : _ case list) =
2,726✔
787
    let _ : case_comp list = comps in
209✔
788
    let _ : case_val list = vals in
789
    let wrap (type a) comps vals : a case -> case_comp list * case_val list =
790
      let _ : case_comp list = comps in
143✔
791
      let _ : case_val list = vals in
792
      fun case ->
793
        match case with
143✔
794
        | { c_lhs = { pat_desc = Tpat_value p }; _ } ->
140✔
795
          ( comps
796
          , { c_lhs = (p :> pattern); c_rhs = case.c_rhs; c_guard = case.c_guard } :: vals
797
          )
798
        | { c_lhs = { pat_desc = Tpat_any }; _ } -> comps, (case :> case_val) :: vals
×
799
        | { c_lhs = { pat_desc = Tpat_var _ }; _ } -> comps, (case :> case_val) :: vals
×
800
        | { c_lhs = { pat_desc = Tpat_alias _ }; _ } -> comps, (case :> case_val) :: vals
×
801
        | { c_lhs = { pat_desc = Tpat_constant _ }; _ } ->
×
802
          comps, (case :> case_val) :: vals
803
        | { c_lhs = { pat_desc = Tpat_construct _ }; _ } ->
×
804
          comps, (case :> case_val) :: vals
805
        | { c_lhs = { pat_desc = Tpat_variant _ }; _ } ->
×
806
          comps, (case :> case_val) :: vals
807
        | { c_lhs = { pat_desc = Tpat_record _ }; _ } -> comps, (case :> case_val) :: vals
×
808
        | { c_lhs = { pat_desc = Tpat_array _ }; _ } -> comps, (case :> case_val) :: vals
×
809
        | { c_lhs = { pat_desc = Tpat_lazy _ }; _ } -> comps, (case :> case_val) :: vals
×
810
        (* | { c_lhs = { pat_desc = Tpat_value _ }; _ } -> (case :> case_comp) :: comps, vals *)
811
        | { c_lhs = { pat_desc = Tpat_exception _ }; _ } ->
3✔
812
          (case :> case_comp) :: comps, vals
813
        | { c_lhs = { pat_desc = Tpat_or _ }; _ } ->
×
814
          failwith "Or-patterns are not yet implemented"
815
        | { c_lhs; _ } ->
×
816
          (* Format.eprintf "%a\n%!" My_printtyped.pattern c_lhs; *)
817
          Format.eprintf
818
            "Unsupported pattern: tag = %d, is_block = %b\n"
819
            Obj.(tag @@ repr c_lhs)
×
820
            Obj.(is_block @@ repr c_lhs);
×
821
          assert false
×
822
    in
823
    match cases with
824
    | h :: tl -> split (wrap comps vals h) tl
143✔
825
    | [] -> List.rev comps, List.rev vals
66✔
826
  in
827
  T
828
    (fun ctx loc e k ->
829
      match e.exp_desc with
2,944✔
830
      | Texp_match (e, cases, _) ->
66✔
831
        ctx.matched <- ctx.matched + 1;
832
        let comp_cases, val_cases = split ([], []) cases in
833
        (* log
834
           "There are %d comp cases and %d val cases"
835
           (List.length comp_cases)
836
           (List.length val_cases); *)
837
        k
66✔
838
        |> fexpr ctx loc e
839
        |> fcomp_cases ctx loc comp_cases
61✔
840
        |> fval_cases ctx loc val_cases
61✔
841
      | _ -> fail loc "texp_match")
2,878✔
842
;;
843

844
[%%else]
845

846
let texp_match (T fexpr) (T fcomp_cases) (T fval_cases) =
847
  T
848
    (fun ctx loc e k ->
849
      match e.Typedtree.exp_desc with
850
      | Texp_match (e, ccases, vcases, _) ->
851
        let ccases, vcases =
852
          List.fold_left
853
            (fun (cacc, vacc) c ->
854
              match c.c_lhs.pat_desc with
855
              | Tpat_value v ->
856
                cacc, { c with c_lhs = (v :> value general_pattern) } :: vacc
857
              | _ -> cacc, vacc)
858
            ([], vcases)
859
            ccases
860
        in
861
        ctx.matched <- ctx.matched + 1;
862
        k |> fexpr ctx loc e |> fcomp_cases ctx loc ccases |> fval_cases ctx loc vcases
863
      | _ -> fail loc "texp_match")
864
;;
865

866
[%%endif]
867

868
let texp_ite (T pred) (T fthen) (T felse) =
869
  T
672✔
870
    (fun ctx loc e k ->
871
      match e.exp_desc with
7,895✔
872
      | Texp_ifthenelse (p, thenb, elseb) ->
185✔
873
        ctx.matched <- ctx.matched + 1;
874
        k |> pred ctx loc p |> fthen ctx loc thenb |> felse ctx loc elseb
77✔
875
      | _ -> fail loc "texp_ite")
7,710✔
876
;;
877

878
[%%if ocaml_version < (5, 0, 0)]
879

880
let texp_try (T fexpr) (T fcases) =
881
  T
56✔
882
    (fun ctx loc e k ->
883
      match e.exp_desc with
1,459✔
884
      | Texp_try (e, cases) ->
3✔
885
        ctx.matched <- ctx.matched + 1;
886
        k |> fexpr ctx loc e |> fcases ctx loc cases
3✔
887
      | _ -> fail loc "texp_try")
1,456✔
888
;;
889

890
[%%else]
891

892
let texp_try (T fexpr) (T fcases) =
893
  T
894
    (fun ctx loc e k ->
895
      match e.exp_desc with
896
      | Typedtree.Texp_try (e, cases, _) ->
897
        (* TODO: support effects *)
898
        ctx.matched <- ctx.matched + 1;
899
        k |> fexpr ctx loc e |> fcases ctx loc cases
900
      | _ -> fail loc "texp_try")
901
;;
902

903
[%%endif]
904

905
let texp_record (T fext) (T ffields) =
906
  T
1,467✔
907
    (fun ctx loc e k ->
908
      match e.exp_desc with
1,467✔
909
      | Texp_record { fields; extended_expression; _ } ->
19✔
910
        ctx.matched <- ctx.matched + 1;
911
        k |> fext ctx loc extended_expression |> ffields ctx loc fields
19✔
912
      | _ -> fail loc "texp_record")
1,448✔
913
;;
914

915
let texp_field (T fexpr) (T fdesc) =
916
  T
56✔
917
    (fun ctx loc e k ->
918
      match e.exp_desc with
51✔
919
      | Texp_field (e, _, desc) ->
17✔
920
        ctx.matched <- ctx.matched + 1;
921
        k |> fexpr ctx loc e |> fdesc ctx loc desc
17✔
922
      | _ -> fail loc "texp_field")
34✔
923
;;
924

925
let label_desc (T fname) =
926
  T
112✔
927
    (fun ctx loc e k ->
928
      match e with
95✔
929
      | { Types.lbl_name; _ } ->
95✔
930
        ctx.matched <- ctx.matched + 1;
931
        k |> fname ctx loc lbl_name)
932
;;
933

934
let rld_kept =
935
  T
936
    (fun ctx loc e k ->
937
      match e with
56✔
938
      | Kept _ ->
×
939
        ctx.matched <- ctx.matched + 1;
940
        k
941
      | _ -> fail loc "rld_kept")
56✔
942
;;
943

944
let rld_overriden (T flident) (T fexpr) =
945
  T
112✔
946
    (fun ctx loc e k ->
947
      match e with
95✔
948
      | Overridden ({ txt = lident }, e) ->
95✔
949
        ctx.matched <- ctx.matched + 1;
950
        k |> flident ctx loc lident |> fexpr ctx loc e
85✔
951
      | _ -> fail loc "rld_overriden")
×
952
;;
953

954
let value_binding (T fpat) (T fexpr) =
955
  T
1,573✔
956
    (fun ctx loc { vb_pat; vb_expr } k ->
957
      ctx.matched <- ctx.matched + 1;
119✔
958
      k |> fpat ctx loc vb_pat |> fexpr ctx loc vb_expr)
117✔
959
;;
960

961
(*   let hack0 (T path0) =
962
     T
963
     (fun ctx loc x k ->
964
     match x.Types.val_type.Types.desc with
965
     | Tconstr (path, [], _) ->
966
     ctx.matched <- ctx.matched + 1;
967
     path0 ctx loc path k
968
     | _ -> fail loc "hack0")
969
     ;;
970

971
     let hack1 ?(on_vd = drop) (T path0) =
972
     T
973
     (fun ctx loc x k ->
974
     match x.exp_desc with
975
     | Texp_ident (path, _, vd) ->
976
     ctx.matched <- ctx.matched + 1;
977
     let (T fvd) = on_vd in
978
     k |> path0 ctx loc path |> fvd ctx loc vd
979
     | _ -> fail loc "texp_ident")
980
     ;;
981

982
     let __ path = hack1 __ path *)
983
let rec core_typ (T ftexpr) = T (fun ctx loc x k -> ftexpr ctx loc x.ctyp_type k)
166✔
984

985
let rec typ_constr (T fpath) (T fargs) =
986
  let rec helper ctx loc x k =
661✔
987
    (* Format.printf "typ = %a\n%!" Printtyp.type_expr x; *)
988
    match Types.get_desc x with
2,374✔
989
    | Tconstr (path, args, _) ->
1,277✔
990
      ctx.matched <- ctx.matched + 1;
991
      k |> fpath ctx loc path |> fargs ctx loc args
205✔
992
    | Tlink arg -> helper ctx loc arg k
×
993
    | _ -> fail loc "typ_constr"
1,097✔
994
  in
995
  T helper
996
;;
997

998
let rec typ_arrow (T l) (T r) =
999
  let rec helper ctx loc x k =
338✔
1000
    (* Format.printf "typ = %a\n%!" Printtyp.type_expr x; *)
1001
    match Types.get_desc x with
22✔
1002
    | Tarrow (_, tl, tr, _) ->
22✔
1003
      ctx.matched <- ctx.matched + 1;
1004
      k |> l ctx loc tl |> r ctx loc tr
22✔
1005
    | _ -> fail loc "typ_arrow"
×
1006
  in
1007
  T helper
1008
;;
1009

1010
let typ_kind_abstract =
1011
  T
1012
    (fun ctx loc x k ->
1013
      match x with
67✔
1014
      | Typedtree.Ttype_abstract ->
8✔
1015
        ctx.matched <- ctx.matched + 1;
1016
        k
1017
      | _ -> fail loc "typ_kind_abstract")
59✔
1018
;;
1019

1020
let typ_kind_open =
1021
  T
1022
    (fun ctx loc x k ->
1023
      match x with
67✔
1024
      | Typedtree.Ttype_open ->
×
1025
        ctx.matched <- ctx.matched + 1;
1026
        k
1027
      | _ -> fail loc "typ_kind_open")
67✔
1028
;;
1029

1030
let typ_kind_variant =
1031
  T
1032
    (fun ctx loc x k ->
1033
      match x with
59✔
1034
      | Typedtree.Ttype_variant _ ->
49✔
1035
        ctx.matched <- ctx.matched + 1;
1036
        k
1037
      | _ -> fail loc "typ_kind_variant")
10✔
1038
;;
1039

1040
let typ_kind_record (T flabels) =
1041
  T
54✔
1042
    (fun ctx loc x k ->
1043
      match x with
10✔
1044
      | Typedtree.Ttype_record labels ->
10✔
1045
        ctx.matched <- ctx.matched + 1;
1046
        k |> flabels ctx loc labels
1047
      | _ -> fail loc "typ_kind_record")
×
1048
;;
1049

1050
(* Structure *)
1051

1052
let tstr_attribute (T fattr) =
1053
  T
40✔
1054
    (fun ctx loc str k ->
1055
      match str.str_desc with
40✔
1056
      | Tstr_attribute attr ->
15✔
1057
        ctx.matched <- ctx.matched + 1;
1058
        k |> fattr ctx loc attr
1059
      | _ -> fail loc "tstr_attribute")
25✔
1060
;;
1061

1062
let tsig_attribute (T fattr) =
1063
  T
3✔
1064
    (fun ctx loc str k ->
1065
      match str.sig_desc with
3✔
1066
      | Tsig_attribute attr ->
2✔
1067
        ctx.matched <- ctx.matched + 1;
1068
        k |> fattr ctx loc attr
1069
      | _ -> fail loc "tsig_attribute")
1✔
1070
;;
1071

1072
let tsig_val_name (T fname) =
1073
  T
×
1074
    (fun ctx loc str k ->
1075
      match str.sig_desc with
×
1076
      | Tsig_value { val_id = txt } ->
×
1077
        ctx.matched <- ctx.matched + 1;
1078
        k |> fname ctx loc txt
1079
      | _ -> fail loc "tsig_val_name")
×
1080
;;
1081

1082
let attribute (T fname) (T fpayload) =
1083
  T
43✔
1084
    (fun ctx loc attr k ->
1085
      let open Parsetree in
17✔
1086
      k |> fname ctx loc attr.attr_name.txt |> fpayload ctx loc attr.attr_payload)
17✔
1087
;;
1088

1089
let payload_stru (T fstru) =
1090
  T
43✔
1091
    (fun ctx loc x k ->
1092
      match x with
17✔
1093
      | Parsetree.PStr stru -> k |> fstru ctx loc stru
17✔
1094
      | _ -> fail loc "payload_stru")
×
1095
;;
1096

1097
let pstr_eval (T f) =
1098
  T
43✔
1099
    (fun ctx loc x k ->
1100
      match x.Parsetree.pstr_desc with
17✔
1101
      | Parsetree.Pstr_eval (e, _) -> k |> f ctx loc e
17✔
1102
      | _ -> fail loc "pstr_eval")
×
1103
;;
1104

1105
let pexp_constant (T f) =
1106
  T
43✔
1107
    (fun ctx loc e k ->
1108
      match e.Parsetree.pexp_desc with
17✔
1109
      | Parsetree.Pexp_constant e -> k |> f ctx loc e
13✔
1110
      | _ -> fail loc "pexp_constant")
4✔
1111
;;
1112

1113
[%%if ocaml_version < (5, 0, 0)]
1114

1115
let pexp_function_cases (T fargs) (T fcases) =
1116
  let open Parsetree in
3✔
1117
  let rec helper acc ctx loc x k =
1118
    match x.pexp_desc with
9✔
1119
    | Pexp_fun (Asttypes.Nolabel, None, pat, rhs) -> helper (pat :: acc) ctx loc rhs k
6✔
1120
    | Pexp_function cases -> k |> fargs ctx loc (List.rev acc) |> fcases ctx loc cases
3✔
1121
    | _ -> fail loc "pexp_function_cases"
×
1122
  in
1123
  T (helper [])
3✔
1124
;;
1125

1126
let pexp_function_body (T fargs) (T fcases) =
1127
  let open Parsetree in
1✔
1128
  let rec helper acc ctx loc x k =
1129
    match x.pexp_desc with
3✔
1130
    | Pexp_fun (Asttypes.Nolabel, None, pat, rhs) -> helper (pat :: acc) ctx loc rhs k
2✔
1131
    | _ -> k |> fargs ctx loc (List.rev acc) |> fcases ctx loc x
1✔
1132
  in
1133
  T (helper [])
1✔
1134
;;
1135

1136
let pconst_string (T fstring) =
1137
  T
43✔
1138
    (fun ctx loc x k ->
1139
      match x with
13✔
1140
      | Parsetree.Pconst_string (s, _, _) -> k |> fstring ctx loc s
13✔
1141
      | _ -> fail loc "pconst_string")
×
1142
;;
1143

1144
[%%else]
1145

1146
let pexp_function_body (T fargs) (T fbody) =
1147
  let open Parsetree in
1148
  T
1149
    (fun ctx loc x k ->
1150
      match x.pexp_desc with
1151
      | Pexp_function (params, _, Pfunction_body e) ->
1152
        let args =
1153
          List.map
1154
            (fun p ->
1155
              match p.pparam_desc with
1156
              | Pparam_val (Nolabel, _, pat) -> pat
1157
              | Pparam_newtype _ | _ -> fail loc "pexp_function_body: params")
1158
            params
1159
        in
1160
        k |> fargs ctx loc args |> fbody ctx loc e
1161
      | _ -> fail loc "pexp_function_body")
1162
;;
1163

1164
let pexp_function_cases (T fargs) (T fcases) =
1165
  let open Parsetree in
1166
  T
1167
    (fun ctx loc x k ->
1168
      match x.pexp_desc with
1169
      | Pexp_function (params, _, Pfunction_cases (cases, _, _)) ->
1170
        let args =
1171
          List.map
1172
            (fun p ->
1173
              match p.pparam_desc with
1174
              | Pparam_val (Nolabel, _, pat) -> pat
1175
              | Pparam_newtype _ | _ -> fail loc "pexp_function_cases: params")
1176
            params
1177
        in
1178
        k |> fargs ctx loc args |> fcases ctx loc cases
1179
      | _ -> fail loc "pexp_function_cases")
1180
;;
1181

1182
let pconst_string (T fstring) =
1183
  T
1184
    (fun ctx loc x k ->
1185
      match x.Parsetree.pconst_desc with
1186
      | Parsetree.Pconst_string (s, _, _) -> k |> fstring ctx loc s
1187
      | _ -> fail loc "pconst_string")
1188
;;
1189

1190
[%%endif]
1191

1192
let pexp_apply (T f) (T fargs) =
1193
  let open Parsetree in
1✔
1194
  let helper ctx loc x k =
1195
    match x.pexp_desc with
1✔
1196
    | Pexp_apply (efun, eargs) -> k |> f ctx loc efun |> fargs ctx loc eargs
1✔
1197
    | _ -> fail loc "pexp_apply"
×
1198
  in
1199
  T helper
1200
;;
1201

1202
let tstr_docattr on_str =
1203
  tstr_attribute
40✔
1204
    (attribute
40✔
1205
       drop
1206
       (payload_stru (pstr_eval (pexp_constant (pconst_string on_str)) ^:: nil)))
40✔
1207
;;
1208

1209
let tsig_docattr on_str =
1210
  tsig_attribute
3✔
1211
    (attribute
3✔
1212
       drop
1213
       (payload_stru (pstr_eval (pexp_constant (pconst_string on_str)) ^:: nil)))
3✔
1214
;;
1215

1216
type context = Ast_pattern0.context
1217

1218
let of_func f = T f
×
1219
let to_func (T f) = f
×
1220
let fail = fail
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc