package p4spectec

  1. Overview
  2. Docs
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/interp_pl/ctx.ml.html

Source file ctx.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
open Domain.Lib
open Lang
open Pl
module Typdef = Runtime.Type.Typdef
open Runtime.Dynamic_Pl
open Envs
open Interp_common.Error
open Interp_common.Backtrace
open Util.Source

(* Error *)

let error_undef (at : region) (kind : string) (id : string) =
  error at (Format.asprintf "%s `%s` is undefined" kind id)

let back_undef (at : region) (kind : string) (id : string) =
  back_err at (Format.asprintf "%s `%s` is undefined" kind id)

let error_dup (at : region) (kind : string) (id : string) =
  error at (Format.asprintf "%s `%s` was already defined" kind id)

let back_dup (at : region) (kind : string) (id : string) =
  back_err at (Format.asprintf "%s `%s` was already defined" kind id)

module Make () = struct
  (* Cursor *)

  type cursor = Global | Local

  (* Mode *)

  let is_det : bool ref = ref false

  (* Context *)

  (* Global layer *)

  type global = {
    (* Map from syntax ids to type definitions *)
    tdtbl : TDTbl.t;
    (* Map from relation ids to relations *)
    rtbl : RTbl.t;
    (* Map from function ids to functions *)
    ftbl : FTbl.t;
  }

  (* Local layer *)

  type local =
    | Empty
    | Rel of {
        (* Relation name *)
        rid : RId.t;
        (* Input values *)
        values_input : value list;
        (* Map from variables to values *)
        venv : VEnv.t;
      }
    | Func of {
        (* Function name *)
        fid : FId.t;
        (* Input values *)
        values_input : value list;
        (* Map from syntax ids to type definitions *)
        tdenv : TDEnv.t;
        (* Map from function ids to functions *)
        fenv : FEnv.t;
        (* Map from variables to values *)
        venv : VEnv.t;
      }

  type t = { global : global; local : local }

  (* Global constructor *)

  let global : global =
    let tdtbl = TDTbl.create ~size:500 in
    let rtbl = RTbl.create ~size:500 in
    let ftbl = FTbl.create ~size:500 in
    { tdtbl; rtbl; ftbl }

  (* Adders for globals *)

  let add_typdef_global (tid : TId.t) (td : Typdef.t) : unit =
    if TDTbl.find_opt tid global.tdtbl |> Option.is_some then
      error_dup tid.at "type" tid.it;
    TDTbl.add tid td global.tdtbl

  let add_rel_global (rid : RId.t) (rel : Rel.t) : unit =
    if RTbl.find_opt rid global.rtbl |> Option.is_some then
      error_dup rid.at "relation" rid.it;
    RTbl.add rid rel global.rtbl

  let add_func_global (fid : FId.t) (func : Func.t) : unit =
    if FTbl.find_opt fid global.ftbl |> Option.is_some then
      error_dup fid.at "function" fid.it;
    FTbl.add fid func global.ftbl

  (* Global initializer *)

  let load_def (def : def) : unit =
    match def.node.it with
    | ExternTypD id ->
        let td = Typdef.Extern in
        add_typdef_global id td
    | TypD (id, tparams, deftyp) ->
        let td = Typdef.Defined (tparams, deftyp) in
        add_typdef_global id td
    | VarD _ -> ()
    | ExternRelD (id, rel_signature, _) ->
        let rel = Rel.Extern rel_signature in
        add_rel_global id rel
    | RelD (id, rel_signature, exps_match, block, elseblock_opt) ->
        let rel =
          Rel.Defined (rel_signature, exps_match, block, elseblock_opt)
        in
        add_rel_global id rel
    | ExternDecD (id, tparams, params, typ) ->
        let func = Func.Extern (tparams, params, typ) in
        add_func_global id func
    | BuiltinDecD (id, tparams, params, typ) ->
        let func = Func.Builtin (tparams, params, typ) in
        add_func_global id func
    | TableDecD (id, params, typ, tablerows) ->
        let func = Func.Table (params, typ, tablerows) in
        add_func_global id func
    | FuncDecD (id, tparams, params, typ, block, elseblock_opt) ->
        let func = Func.Defined (tparams, params, typ, block, elseblock_opt) in
        add_func_global id func

  let init ~(det : bool) (spec : spec) : unit =
    is_det := det;
    List.iter load_def spec

  (* Constructor *)

  let empty () : t = { global; local = Empty }

  (* Finders *)

  (* Finders for input values *)

  let find_values_input_opt (ctx : t) : Value.t list option =
    match ctx.local with
    | Empty -> None
    | Rel { values_input; _ } -> Some values_input
    | Func { values_input; _ } -> Some values_input

  let find_values_input (ctx : t) : Value.t list =
    match find_values_input_opt ctx with
    | Some values_input -> values_input
    | None ->
        back_err no_region "cannot find input values in empty local context"

  (* Finders for values *)

  let find_value_opt (ctx : t) (var : Var.t) : Value.t option =
    match ctx.local with
    | Empty -> None
    | Rel { venv; _ } -> VEnv.find_opt var venv
    | Func { venv; _ } -> VEnv.find_opt var venv

  let find_value (ctx : t) (var : Var.t) : Value.t =
    match find_value_opt ctx var with
    | Some value -> value
    | None ->
        let id, _ = var in
        back_undef id.at "value" (Var.to_string var)

  let bound_value (ctx : t) (var : Var.t) : bool =
    find_value_opt ctx var |> Option.is_some

  (* Finders for type definitions *)

  let find_typdef_opt (ctx : t) (tid : TId.t) : Typdef.t option =
    let tdenv =
      match ctx.local with
      | Empty | Rel _ -> TDEnv.empty
      | Func { tdenv; _ } -> tdenv
    in
    match TDEnv.find_opt tid tdenv with
    | Some td -> Some td
    | None -> TDTbl.find_opt tid ctx.global.tdtbl

  let find_typdef (ctx : t) (tid : TId.t) : Typdef.t =
    match find_typdef_opt ctx tid with
    | Some td -> td
    | None -> back_undef tid.at "type" tid.it

  let find_defined_typdef (ctx : t) (tid : TId.t) : tparam list * deftyp =
    match find_typdef ctx tid with
    | Param | Extern | Defining _ -> back_undef tid.at "defined type" tid.it
    | Defined (tparams, deftyp) -> (tparams, deftyp)

  let bound_typdef (ctx : t) (tid : TId.t) : bool =
    find_typdef_opt ctx tid |> Option.is_some

  (* Finders for rules *)

  let find_rel_opt (ctx : t) (rid : RId.t) : Rel.t option =
    RTbl.find_opt rid ctx.global.rtbl

  let find_rel (ctx : t) (rid : RId.t) : Rel.t =
    match find_rel_opt ctx rid with
    | Some rel -> rel
    | None -> back_undef rid.at "relation" rid.it

  let find_rel_signature_opt (ctx : t) (rid : RId.t) :
      (nottyp * Hints.Input.t) option =
    find_rel_opt ctx rid |> Option.map Rel.get_signature

  let find_rel_signature (ctx : t) (rid : RId.t) : nottyp * Hints.Input.t =
    match find_rel_signature_opt ctx rid with
    | Some (nottyp, inputs) -> (nottyp, inputs)
    | None -> back_undef rid.at "relation" rid.it

  let bound_rel (ctx : t) (rid : RId.t) : bool =
    find_rel_opt ctx rid |> Option.is_some

  (* Finders for definitions *)

  let find_func_opt (ctx : t) (fid : FId.t) : (cursor * Func.t) option =
    let fenv =
      match ctx.local with
      | Empty | Rel _ -> FEnv.empty
      | Func { fenv; _ } -> fenv
    in
    match FEnv.find_opt fid fenv with
    | Some func -> Some (Local, func)
    | None ->
        FTbl.find_opt fid ctx.global.ftbl
        |> Option.map (fun func -> (Global, func))

  let find_func (ctx : t) (fid : FId.t) : cursor * Func.t =
    match find_func_opt ctx fid with
    | Some (cursor, func) -> (cursor, func)
    | None -> back_undef fid.at "function" fid.it

  let find_func_signature_opt (ctx : t) (fid : FId.t) :
      (tparam list * typ list * typ) option =
    find_func_opt ctx fid
    |> Option.map (fun (_, func) -> Func.get_signature func)

  let find_func_signature (ctx : t) (fid : FId.t) : tparam list * typ list * typ
      =
    match find_func_signature_opt ctx fid with
    | Some (tparams, typs, typ) -> (tparams, typs, typ)
    | None -> back_undef fid.at "function" fid.it

  let bound_func (ctx : t) (fid : FId.t) : bool =
    find_func_opt ctx fid |> Option.is_some

  (* Adders *)

  (* Adders for values *)

  let add_value (ctx : t) (var : Var.t) (value : Value.t) : t =
    match ctx.local with
    | Empty ->
        let id, _ = var in
        back_err id.at "cannot add value to empty local context"
    | Rel { rid; values_input; venv } ->
        let venv = VEnv.add var value venv in
        { ctx with local = Rel { rid; values_input; venv } }
    | Func { fid; values_input; tdenv; fenv; venv } ->
        let venv = VEnv.add var value venv in
        { ctx with local = Func { fid; values_input; tdenv; fenv; venv } }

  (* Adders for type definitions *)

  let add_typdef (ctx : t) (tid : TId.t) (td : Typdef.t) : t =
    if bound_typdef ctx tid then back_dup tid.at "type" tid.it;
    match ctx.local with
    | Empty -> back_err tid.at "cannot add type to empty local context"
    | Rel _ -> back_err tid.at "cannot add type to rule context"
    | Func { fid; values_input; tdenv; fenv; venv } ->
        let tdenv = TDEnv.add tid td tdenv in
        { ctx with local = Func { fid; values_input; tdenv; fenv; venv } }

  (* Adders for functions *)

  let add_func (ctx : t) (fid : FId.t) (func : Func.t) : t =
    if bound_func ctx fid then back_dup fid.at "function" fid.it;
    match ctx.local with
    | Empty -> back_err fid.at "cannot add function to empty local context"
    | Rel _ -> back_err fid.at "cannot add function to relation context"
    | Func { fid = fid_local; values_input; tdenv; fenv; venv } ->
        let fenv = FEnv.add fid func fenv in
        {
          ctx with
          local = Func { fid = fid_local; values_input; tdenv; fenv; venv };
        }

  (* Constructors *)

  (* Constructing a local context *)

  let localize (ctx : t) : t = { ctx with local = Empty }

  let localize_rule (ctx : t) (rid : RId.t) (values_input : value list) : t =
    let local = Rel { rid; values_input; venv = VEnv.empty } in
    { ctx with local }

  let localize_func (ctx : t) (fid : FId.t) (values_input : value list)
      (tdenv : TDEnv.t) : t =
    let local =
      Func { fid; values_input; tdenv; fenv = FEnv.empty; venv = VEnv.empty }
    in
    { ctx with local }

  let localize_clear (ctx : t) : t =
    match ctx.local with
    | Empty -> back_err no_region "cannot clear empty local context"
    | Rel { rid; values_input; _ } ->
        { ctx with local = Rel { rid; values_input; venv = VEnv.empty } }
    | Func { fid; values_input; tdenv; fenv; _ } ->
        {
          ctx with
          local = Func { fid; values_input; tdenv; fenv; venv = VEnv.empty };
        }

  (* Constructing sub-contexts *)

  (* Transpose a matrix of values, as a list of value batches
     that are to be each fed into an iterated expression *)

  let transpose (value_matrix : value list list) : value list list =
    match value_matrix with
    | [] -> []
    | row_h :: _ ->
        let width = List.length row_h in
        let cols = Array.make width [] in
        List.iter
          (fun row ->
            check_back_err
              (List.length row = width)
              no_region "cannot transpose a matrix of value batches";
            List.iteri (fun j v -> cols.(j) <- v :: cols.(j)) row)
          (List.rev value_matrix);
        Array.to_list cols

  let sub_opt (ctx : t) (vars : var list) : t option =
    (* First collect the values that are to be iterated over *)
    let values =
      List.map
        (fun (id, _typ, iters) ->
          find_value ctx (id, iters @ [ Il.Opt ]) |> Value.Get.opt)
        vars
    in
    (* Iteration is valid when all variables agree on their optionality *)
    if List.for_all Option.is_some values then
      let values = List.map Option.get values in
      let ctx_sub =
        List.fold_left2
          (fun ctx_sub (id, _typ, iters) value ->
            add_value ctx_sub (id, iters) value)
          ctx vars values
      in
      Some ctx_sub
    else if List.for_all Option.is_none values then None
    else back_err no_region "mismatch in optionality of iterated variables"

  let sub_list (ctx : t) (vars : var list) : t list =
    (* First break the values that are to be iterated over,
       into a batch of values *)
    let values_batch =
      List.map
        (fun (id, _typ, iters) ->
          find_value ctx (id, iters @ [ Il.List ]) |> Value.Get.list)
        vars
      |> transpose
    in
    (* For each batch of values, create a sub-context *)
    List.map
      (fun value_batch ->
        List.fold_left2
          (fun ctx_sub (id, _typ, iters) value ->
            add_value ctx_sub (id, iters) value)
          ctx vars value_batch)
      values_batch
end