package p4spectec
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
P4-SpecTec: A mechanization toolchain for the P4 Programming Language
Install
dune-project
Dependency
Authors
Maintainers
Sources
v0.1.2.tar.gz
md5=1a3bc0a385fe1ecf403c019f49aa6de6
sha512=5d20b5821f33e2a3a5419b208606f27c01511994c2b3b1e1cdf4c077056dfd0aa81682af0720e1060ee2bfb0341918fcc4c53159820205a2bc32b725e5c1a714
doc/src/p4spectec.backend_testgen_neg/mutate.ml.html
Source file mutate.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424open Lang open Il module Type = Runtime.Type module Value = Runtime.Value open Runtime.Testgen_neg open Envs open Domain.Lib open Util.Source module Mixfix = Domain.Mixfix module Mixop = Domain.Mixop (* Kinds of mutations *) type kind = GenFromTyp | MutateList | MixopGroup let string_of_kind = function | GenFromTyp -> "GenFromTyp" | MutateList -> "MutateList" | MixopGroup -> "MixopGroup" (* Option monad *) let ( let* ) = Option.bind (* Helpers for wrapping values *) let wrap_value (typ : typ') (value : value') : value = let vhash = Value.hash_of value in value $$ (no_region, { vid = -1; typ; vhash }) let wrap_value_opt (typ : typ') (value_opt : value' option) : value option = Option.map (wrap_value typ) value_opt (* Type-driven mutation *) let rec gen_from_typ (depth : int) (tdenv : TDEnv.t) (texts : value' list) (typ : typ) : value option = if depth <= 0 then None else gen_from_typ' depth tdenv texts typ and gen_from_typ' (depth : int) (tdenv : TDEnv.t) (texts : value' list) (typ : typ) : value option = let depth = depth - 1 in match typ.it with | BoolT -> [ BoolV true; BoolV false ] |> Rand.random_select |> wrap_value_opt typ.it | NumT `NatT -> [ NumV (`Nat (Bigint.of_int 1)); NumV (`Nat (Bigint.of_int 4)); NumV (`Nat (Bigint.of_int 6)); NumV (`Nat (Bigint.of_int 8)); ] |> Rand.random_select |> wrap_value_opt typ.it | NumT `IntT -> [ NumV (`Int (Bigint.of_int (-2))); NumV (`Int (Bigint.of_int 0)); NumV (`Int (Bigint.of_int 2)); NumV (`Int (Bigint.of_int 3)); ] |> Rand.random_select |> wrap_value_opt typ.it | TextT -> texts |> Rand.random_select |> wrap_value_opt typ.it | VarT (tid, targs) -> ( let td = TDEnv.find_opt tid tdenv in match td with | Some (Defined (tparams, td)) -> ( let theta = List.combine tparams targs |> TDEnv.of_list in match td.it with | PlainT typ -> typ |> Type.Subst.subst_typ theta |> gen_from_typ depth tdenv texts | StructT typfields -> let atoms, typs = List.split typfields in let* values = typs |> Type.Subst.subst_typs theta |> gen_from_typs depth tdenv texts in let valuefields = List.combine atoms values in StructV valuefields |> Option.some |> wrap_value_opt typ.it | VariantT typcases -> let nottyps' = typcases |> List.map (fun (nottyp, _, _) -> Mixfix.map (Type.Subst.subst_typ theta) nottyp.it) in let expand_nottyp' nottyp' = let mixop, typs = Mixfix.split nottyp' in let* values = gen_from_typs depth tdenv texts typs in CaseV (Mixfix.fill mixop values) |> Option.some in List.map expand_nottyp' nottyps' |> List.filter Option.is_some |> List.map Option.get |> Rand.random_select |> wrap_value_opt typ.it) | _ -> None) | TupleT typs_inner -> let* values_inner = gen_from_typs depth tdenv texts typs_inner in TupleV values_inner |> Option.some |> wrap_value_opt typ.it | IterT (_, Opt) when depth = 0 -> OptV None |> Option.some |> wrap_value_opt typ.it | IterT (typ_inner, Opt) -> let choices : value' option list = [ OptV None |> Option.some; (let* value_inner = gen_from_typ depth tdenv texts typ_inner in OptV (Some value_inner) |> Option.some); ] in let* choice = choices |> List.filter Option.is_some |> Rand.random_select in choice |> wrap_value_opt typ.it | IterT (_, List) when depth = 0 -> ListV [] |> Option.some |> wrap_value_opt typ.it | IterT (typ_inner, List) -> let len = Random.int 3 in let* values_inner = List.init len (fun _ -> typ_inner) |> gen_from_typs depth tdenv texts in ListV values_inner |> Option.some |> wrap_value_opt typ.it | FuncT _ -> None and gen_from_typs (depth : int) (tdenv : TDEnv.t) (texts : value' list) (typs : typ list) : value list option = if depth <= 0 then None else List.fold_left (fun values_opt typ -> let* values = values_opt in let* value = gen_from_typ depth tdenv texts typ in Some (values @ [ value ])) (Some []) typs let mutate_type_driven (tdenv : TDEnv.t) (texts : value' list) (value : value) : (kind * value) option = let typ = value.note.typ $ no_region in let depth = Random.int 4 + 1 in let value_opt = gen_from_typ depth tdenv texts typ in Option.map (fun value -> (GenFromTyp, value)) value_opt (* Constructor mutation *) let mutate_mixop (mixopenv : MixopEnv.t) (value : value) : (kind * value) option = let typ = value.note.typ in match typ with | VarT (id, _) -> ( match value.it with | CaseV valuecase -> let mixop, values = Mixfix.split valuecase in let* mixop_family = MixopEnv.find_opt id mixopenv in let mixop_family = Mixops.Family.filter (fun mixop_group -> MixIdSet.exists (Mixop.eq mixop) mixop_group) mixop_family in let* mixop_group = if Mixops.Family.cardinal mixop_family = 0 then None else mixop_family |> Mixops.Family.choose |> Option.some in let* mixop = mixop_group |> MixIdSet.filter (fun mixop_e -> not (Mixop.eq mixop mixop_e)) |> MixIdSet.elements |> Rand.random_select in let value = CaseV (Mixfix.fill mixop values) |> wrap_value typ in (MixopGroup, value) |> Option.some | _ -> assert false) | _ -> assert false (* List mutations *) let rec shuffle_list' (value : value) : value = let typ = value.note.typ in match value.it with | BoolV _ | NumV _ | TextV _ -> value.it |> wrap_value typ | StructV valuefields -> let atoms, values = List.split valuefields in let values_shuffled = List.map shuffle_list' values in let valuefields_shuffled = List.combine atoms values_shuffled in StructV valuefields_shuffled |> wrap_value typ | CaseV valuecase -> let valuecase_shuffled = Mixfix.map shuffle_list' valuecase in CaseV valuecase_shuffled |> wrap_value typ | TupleV values -> let values_shuffled = List.map shuffle_list' values in TupleV values_shuffled |> wrap_value typ | OptV None -> value.it |> wrap_value typ | OptV (Some value) -> let value_shuffled = shuffle_list' value in OptV (Some value_shuffled) |> wrap_value typ | ListV values -> let values_shuffled = Rand.shuffle values in ListV values_shuffled |> wrap_value typ | FuncV _ | ExternV _ -> value.it |> wrap_value typ let shuffle_list (value : value) : value option = let value_shuffled = shuffle_list' value in if Value.eq value value_shuffled then None else Some value_shuffled let rec duplicate_list' (value : value) : value = let typ = value.note.typ in match value.it with | BoolV _ | NumV _ | TextV _ -> value.it |> wrap_value typ | StructV valuefields -> let atoms, values = List.split valuefields in let values_duplicated = List.map duplicate_list' values in let valuefields_duplicated = List.combine atoms values_duplicated in StructV valuefields_duplicated |> wrap_value typ | CaseV valuecase -> let valuecase_duplicated = Mixfix.map duplicate_list' valuecase in CaseV valuecase_duplicated |> wrap_value typ | TupleV values -> let values_duplicated = List.map duplicate_list' values in TupleV values_duplicated |> wrap_value typ | OptV None -> value.it |> wrap_value typ | OptV (Some value) -> let value_duplicated = duplicate_list' value in OptV (Some value_duplicated) |> wrap_value typ | ListV values -> ( match Rand.random_select values with | Some value -> let values = value :: values in ListV values |> wrap_value typ | None -> value.it |> wrap_value typ) | FuncV _ | ExternV _ -> value.it |> wrap_value typ let duplicate_list (value : value) : value option = let value_duplicated = duplicate_list' value in if Value.eq value value_duplicated then None else Some value_duplicated let rec shrink_list' (value : value) : value = let typ = value.note.typ in match value.it with | BoolV _ | NumV _ | TextV _ -> value.it |> wrap_value typ | StructV valuefields -> let atoms, values = List.split valuefields in let values_shrinked = List.map shrink_list' values in let valuefields_shrinked = List.combine atoms values_shrinked in StructV valuefields_shrinked |> wrap_value typ | CaseV valuecase -> let valuecase_shrinked = Mixfix.map shrink_list' valuecase in CaseV valuecase_shrinked |> wrap_value typ | TupleV values -> let values_shrinked = List.map shrink_list' values in TupleV values_shrinked |> wrap_value typ | OptV None -> value.it |> wrap_value typ | OptV (Some value) -> let value_shrinked = shrink_list' value in OptV (Some value_shrinked) |> wrap_value typ | ListV [] -> value.it |> wrap_value typ | ListV values -> let size = Random.int (List.length values) in let values = Rand.random_sample size values in ListV values |> wrap_value typ | FuncV _ | ExternV _ -> value.it |> wrap_value typ let shrink_list (value : value) : value option = let value_shrinked = shrink_list' value in if Value.eq value value_shrinked then None else Some value_shrinked let mutate_list (value : value) : (kind * value) option = let wrap_kind (value_opt : value option) : (kind * value) option = Option.map (fun value -> (MutateList, value)) value_opt in let mutations_list = [ (fun () -> shuffle_list value |> wrap_kind); (fun () -> duplicate_list value |> wrap_kind); (fun () -> shrink_list value |> wrap_kind); ] in let* mutation = Rand.random_select mutations_list in mutation () let mutate_node (tdenv : TDEnv.t) (mixopenv : MixopEnv.t) (texts : value' list) (value : value) : (kind * value) option = match value.it with | ListV _ -> let* mutation = [ (fun () -> mutate_list value); (fun () -> mutate_type_driven tdenv texts value); ] |> Rand.random_select in mutation () | CaseV _ -> let* mutation = [ (fun () -> mutate_mixop mixopenv value); (fun () -> mutate_type_driven tdenv texts value); ] |> Rand.random_select in mutation () | _ -> mutate_type_driven tdenv texts value let mutate_walk (tdenv : TDEnv.t) (mixopenv : MixopEnv.t) (texts : value' list) (value : value) : (kind * value) option = (* Compute the best path to a leaf node in the value subtree *) let key_max = ref min_float in let path_best = ref [] in let rec traverse (path : int list) (value : value) (depth : int) : unit = let weight = 1.0 /. (float_of_int (depth + 1) ** 3.0) in let u = Random.float 1.0 in let key = u ** (1.0 /. weight) in if key > !key_max then ( key_max := key; path_best := List.rev path); match value.it with | BoolV _ | NumV _ | TextV _ | OptV _ | FuncV _ | ExternV _ -> () | StructV valuefields -> List.iteri (fun idx (_, value) -> traverse (idx :: path) value (depth + 1)) valuefields | CaseV valuecase -> let values = Mixfix.args valuecase in List.iteri (fun idx value -> traverse (idx :: path) value (depth + 1)) values | TupleV values | ListV values -> List.iteri (fun idx value -> traverse (idx :: path) value (depth + 1)) values in traverse [] value 0; let kind_found = ref None in (* Rebuild the value tree with a new value at the best path *) let rec rebuild (path : int list) (value : value) : value option = let typ = value.note.typ in match (path, value) with | [], value -> let* kind, value = mutate_node tdenv mixopenv texts value in kind_found := kind |> Option.some; value |> Option.some | idx :: path, value -> ( match value.it with | BoolV _ | NumV _ | TextV _ | OptV _ | FuncV _ | ExternV _ -> value.it |> wrap_value typ |> Option.some | StructV valuefields -> let atoms, values = List.split valuefields in let* values = rebuilds path idx values in let valuefields = List.combine atoms values in StructV valuefields |> wrap_value typ |> Option.some | CaseV valuecase -> let mixop, values = Mixfix.split valuecase in let* values = rebuilds path idx values in CaseV (Mixfix.fill mixop values) |> wrap_value typ |> Option.some | TupleV values -> let* values = rebuilds path idx values in TupleV values |> wrap_value typ |> Option.some | ListV values -> let* values = rebuilds path idx values in ListV values |> wrap_value typ |> Option.some) and rebuilds rest i (values_inner : value list) : value list option = values_inner |> List.mapi (fun j value -> if j = i then rebuild rest value else Some value) |> List.fold_left (fun values_opt value -> let* values = values_opt in let* value = value in Some (values @ [ value ])) (Some []) in let* value = rebuild !path_best value in let* kind = !kind_found in Some (kind, value) (* Find parent node, if any, in the dependency graph *) let find_parent (vdg : Dep.Graph.t) (vid_source : vid) : vid option = let parents = (* for all edges from v *) match Dep.Graph.G.find_opt vdg.edges vid_source with | None -> [] | Some edges -> (* follow Expand edges to source nodes *) Dep.Edges.E.fold (fun (label, vid_target) () acc -> if label = Dep.Edges.Expand then vid_target :: acc else acc) edges [] in assert (List.length parents <= 1); parents |> Rand.random_select (* Entry point for mutation *) let mutate (tdenv : TDEnv.t) (mixopenv : MixopEnv.t) (texts : value' list) (vdg : Dep.Graph.t) (vid_source : vid) : (kind * value * value) option = (* Expand the node randomly *) let expansions = [ (fun () -> find_parent vdg vid_source); (fun () -> vid_source |> Option.some); ] in let expansion = Rand.random_select expansions |> Option.get in let vid_to_mutate = match expansion () with Some vid_parent -> vid_parent | None -> vid_source in (* reassemble value from vid *) let value_to_mutate = Dep.Graph.reassemble_graph vdg VIdMap.empty vid_to_mutate in (* Mutate the node *) let* kind, value_mutated = mutate_walk tdenv mixopenv texts value_to_mutate in (kind, value_to_mutate, value_mutated) |> Option.some let mutates (fuel_mutate : int) (tdenv : TDEnv.t) (mixopenv : MixopEnv.t) (vdg : Dep.Graph.t) (vid_source : vid) : (kind * value * value) list = (* Collect the text pool *) let texts = List.init (vdg.root + 1) Fun.id |> List.filter_map (fun vid -> let* mirror, _ = Dep.Graph.find_node vdg vid in match mirror.it with TextN text -> Some (TextV text) | _ -> None) in let texts = texts @ [ TextV "lazy"; TextV "fox" ] in (* Do mutations *) List.init fuel_mutate (fun _ -> mutate tdenv mixopenv texts vdg vid_source) |> List.filter_map Fun.id
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>