~ubuntu-branches/ubuntu/wily/marionnet/wily

« back to all changes in this revision

Viewing changes to chip/chip_parser_p4.ml

  • Committer: Package Import Robot
  • Author(s): Lucas Nussbaum
  • Date: 2013-03-29 15:57:12 UTC
  • Revision ID: package-import@ubuntu.com-20130329155712-o0b9b96w8av68ktq
Tags: upstream-0.90.6+bzr407
ImportĀ upstreamĀ versionĀ 0.90.6+bzr407

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
(* This file is part of Marionnet, a virtual network laboratory
 
2
   Copyright (C) 2009  Jean-Vincent Loddo
 
3
   Copyright (C) 2009  UniversitĆ© Paris 13
 
4
   Applied an OCaml 3.12 compatibility patch by ClĆ©ment DĆ©moulins in 2011
 
5
 
 
6
   This program is free software: you can redistribute it and/or modify
 
7
   it under the terms of the GNU General Public License as published by
 
8
   the Free Software Foundation, either version 2 of the License, or
 
9
   (at your option) any later version.
 
10
 
 
11
   This program is distributed in the hope that it will be useful,
 
12
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
   GNU General Public License for more details.
 
15
 
 
16
   You should have received a copy of the GNU General Public License
 
17
   along with this program.  If not, see <http://www.gnu.org/licenses/>. *)
 
18
 
 
19
 
 
20
open Camlp4 (* -*- camlp4o -*- *)
 
21
 
 
22
(* ocamlc -I +camlp4 -pp camlp4of.opt camlp4lib.cma chip_parser.p4.ml *)
 
23
 
 
24
(* For a strange bug, Camlp4of goes in a loop (StackOverflow) when a chip
 
25
   with more than 1 port is declared virtual. Thus, in practice, if the chip
 
26
   is virtual, we are limited to 1 port! *)
 
27
 
 
28
module Id = struct
 
29
  let name = "Chip_pa"
 
30
  let version = "$Id: chip_pa.ml,v 0.1 2009/02/16 16:16:16 $"
 
31
end
 
32
 
 
33
module Make (Syntax : Sig.Camlp4Syntax) = struct
 
34
  open Sig
 
35
  include Syntax
 
36
 
 
37
  (* Make a multiple application. *)
 
38
  let apply _loc f xs = List.fold_left (fun f x -> <:expr< $f$ $x$>>) f xs
 
39
 
 
40
  (* Make a string list ast from... a string list. *)
 
41
  let rec string_list_of_string_list _loc = function
 
42
   | []    -> <:expr< [] >>
 
43
   | x::xs -> let xs' = string_list_of_string_list _loc xs in <:expr< $str:x$ :: $xs'$>>
 
44
 
 
45
  let rec expr_of_expr_list _loc = function
 
46
   | []    -> <:expr< [] >>
 
47
   | x::xs -> let xs' = expr_of_expr_list _loc xs in <:expr< $x$ :: $xs'$>>
 
48
 
 
49
  (* Make a multiple abstraction to a class_expr. *)
 
50
  let lambda_class_expr _loc xs body = List.fold_right (fun x b -> <:class_expr< fun $x$ -> $b$>>) xs body
 
51
 
 
52
  (* Make a multiple abstraction to a type. *)
 
53
  let lambda_ctyp _loc xs body = List.fold_right (fun x b -> <:ctyp< ($x$ -> $b$)>>) xs body
 
54
 
 
55
  (* Make a fresh identifier (string). *)
 
56
  let fresh_var_name ~blacklist ~prefix =
 
57
   let rec loop n =
 
58
    let candidate = (prefix^(string_of_int n)) in
 
59
    if not (List.mem candidate blacklist) then candidate else loop (n+1)
 
60
   in
 
61
   if (List.mem prefix blacklist) then (loop 0) else prefix
 
62
 
 
63
  let fresh_var_names ~blacklist ~prefixes =
 
64
   let r = List.fold_left (fun l p -> (fresh_var_name ~blacklist:(l@blacklist) ~prefix:p) :: l) [] prefixes in
 
65
   List.rev r
 
66
 
 
67
  (* List.combine for 3 lists *)
 
68
  let rec combine3 l1 l2 l3 = match (l1,l2,l3) with
 
69
  | []    , []    , []     -> []
 
70
  | x1::r1, x2::r2, x3::r3 -> (x1,x2,x3)::(combine3 r1 r2 r3)
 
71
  | _ -> raise (Invalid_argument "combine3")
 
72
 
 
73
  let rec combine4 l1 l2 l3 l4 = match (l1,l2,l3,l4) with
 
74
  | []    , []    , []   , []  -> []
 
75
  | x1::r1, x2::r2, x3::r3, x4::r4 -> (x1,x2,x3,x4)::(combine4 r1 r2 r3 r4)
 
76
  | _ -> raise (Invalid_argument "combine4")
 
77
 
 
78
  (* Check unicity in a string list. Raises a failure with the duplicated string. *)
 
79
  let rec check_unicity = function
 
80
  | []   -> ()
 
81
  | x::r -> if (List.mem x r) then failwith ("Duplicated port name '"^x^"'")
 
82
                              else (check_unicity r)
 
83
 
 
84
 
 
85
  EXTEND Gram
 
86
    GLOBAL: str_item;
 
87
 
 
88
    where:
 
89
      [ [  "complement"; w = LIST0 class_str_item ; "end" -> w ] ] ;
 
90
 
 
91
    port_ident:
 
92
      [ [  "("; x = LIDENT ; ":"; t = ctyp; ")" -> (x, Some t)
 
93
        |       x = LIDENT ; ":"; t = ctyp      -> (x, Some t)
 
94
        |       x = LIDENT                      -> (x, None  )  ] ] ;
 
95
 
 
96
    str_item: FIRST
 
97
      [ [ "chip"; virt = OPT "virtual"; class_name = LIDENT; user_parameters = LIST0 [ x=patt -> x];  ":" ;
 
98
 
 
99
          input_ports  = [ "(" ; l = LIST0 [ x = port_ident -> x] SEP ","; ")" -> l ];
 
100
 
 
101
          output_ports = [ "->"; "("; l = LIST0 [ x = port_ident -> x] SEP "," ; ")"; "=" -> l
 
102
                         | "->"; "unit"; "=" -> []
 
103
                         | "="               -> [] ];
 
104
 
 
105
          e = expr ; w = OPT where ->
 
106
 
 
107
          (* Class type *)
 
108
          let is_virtual = (virt <> None) in
 
109
 
 
110
          (* Are there where clauses: *)
 
111
          let user_complement_section = match w with None -> [] | Some w -> w in
 
112
 
 
113
          (* Port names (inputs + outputs) *)
 
114
          let is_source = (input_ports = []) in
 
115
          let inputs  = List.map fst input_ports  in
 
116
          let outputs = List.map fst output_ports in
 
117
          let port_names = List.append inputs outputs in
 
118
          let () = check_unicity port_names in
 
119
 
 
120
          let ancestor = match input_ports, output_ports with
 
121
            | [],[] ->  failwith "Autistic chips not allowed!"
 
122
            | [], _ -> "source"
 
123
            | _ ,[] -> "sink"
 
124
            | _ ,_  -> "relay"
 
125
          in
 
126
 
 
127
          (* Support functions and values *)
 
128
 
 
129
          let set_alone_application  a = <:expr< self#$lid:"set_alone_" ^a$ $lid:a$ >>  in
 
130
          let connect_application    a = <:expr< self#$lid:"connect_"   ^a$ $lid:a$ >>  in
 
131
          let disconnect_application a = <:expr< self#$lid:"disconnect_"^a$ >>          in
 
132
 
 
133
          (* Class type variables *)
 
134
          let ctv =
 
135
            let type_variables = List.map (fun i -> <:ctyp< '$lid:i$ >>) port_names in
 
136
            Ast.tyCom_of_list type_variables
 
137
          in
 
138
 
 
139
          (* Inherit section *)
 
140
          let inherit_section =  [ <:class_str_item< inherit $lid:ancestor$ ?name system >> ] in
 
141
 
 
142
          (* Tool for creating a pattern depending on the length of a list of identifiers
 
143
             encoded by strings. If the list is empty, the pattern () is built. If the list is
 
144
             a singleton, the pattern is simply itself. If the list has almost two elements,
 
145
             a tuple pattern is created. *)
 
146
          let pattern_of_string_list = function
 
147
           | []  -> <:patt< () >>
 
148
           | [x] -> <:patt< $lid:x$>>
 
149
           |  l  -> Ast.PaTup (_loc, Ast.paCom_of_list (List.map (fun x-> <:patt< $lid:x$>>) l))
 
150
          in
 
151
 
 
152
          let type_of_ctyp_list = function
 
153
           | []  -> <:ctyp< unit >>
 
154
           | [t] -> t
 
155
           |  l  -> Ast.TyTup (_loc, Ast.tySta_of_list l)
 
156
          in
 
157
 
 
158
          (* Firmware section *)
 
159
 
 
160
          let domains =
 
161
            let mill =
 
162
             fun (x,ot) -> match ot with None -> Ast.TyLab(_loc,x,<:ctyp< _ >>) | Some t -> Ast.TyLab(_loc,x,t) in
 
163
            List.map mill input_ports
 
164
          in
 
165
 
 
166
          let codomain =
 
167
            let mill = fun (_,ot) -> match ot with None -> <:ctyp< _ >> | Some t -> t in
 
168
            type_of_ctyp_list (List.map mill output_ports)
 
169
          in
 
170
 
 
171
          let signature = lambda_ctyp _loc domains codomain in
 
172
 
 
173
          let firmware =
 
174
            let pl = List.map (fun i -> Ast.PaLab (_loc, i, <:patt< >>)) inputs             in
 
175
            let firmware_expr   = List.fold_right (fun p e -> <:expr< fun $p$ -> $e$ >>) pl e in
 
176
            let firmware_method =
 
177
              match is_source with
 
178
              | false -> <:class_str_item< method firmware : $signature$ = $firmware_expr$ >>
 
179
              | true  -> <:class_str_item< method firmware = $firmware_expr$ >>
 
180
            in
 
181
            [firmware_method]
 
182
          in
 
183
 
 
184
          (* in_port fields section *)
 
185
          let in_port_fields =
 
186
            let mill a = <:class_str_item< val $lid:a$ = (new in_port ~name:$str:a$ ?wire:$lid:a$ ()) >> in
 
187
            List.map mill inputs
 
188
          in
 
189
 
 
190
          (* out_port fields section *)
 
191
          let out_port_fields =
 
192
            let mill x = <:class_str_item< val $lid:x$ = (let wire = $lid:x$ in new out_port ~name:$str:x$ ?wire ()) >> in
 
193
            List.map mill outputs
 
194
          in
 
195
 
 
196
          let port_names_fields =
 
197
            let  in_port_names = string_list_of_string_list _loc inputs  in
 
198
            let out_port_names = string_list_of_string_list _loc outputs in
 
199
            [ <:class_str_item< val  in_port_names =  $in_port_names$ >> ;
 
200
              <:class_str_item< val out_port_names = $out_port_names$ >> ]
 
201
          in
 
202
 
 
203
          (* connect_? methods for input ports *)
 
204
          let connect_i =
 
205
            let mill a =
 
206
             let b  = fresh_var_name ~blacklist:port_names ~prefix:a in
 
207
             let tv = <:ctyp< '$lid:a$ >> in
 
208
             let tv'= <:ctyp< '$lid:b$ >> in
 
209
             let t  = <:ctyp< ($tv'$,$tv$) wire -> unit >> in
 
210
             let method_type = Ast.TyPol (_loc, Ast.TyQuo(_loc,b), t) in
 
211
             <:class_str_item< method $lid:"connect_"^a$ : $method_type$ = $lid:a$#connect >> in
 
212
            List.map mill inputs
 
213
          in
 
214
 
 
215
          (* connect_? methods for output ports *)
 
216
          let connect_o =
 
217
            let mill a =
 
218
             let b  = fresh_var_name ~blacklist:port_names ~prefix:a in
 
219
             let tv = <:ctyp< '$lid:a$ >> in
 
220
             let tv'= <:ctyp< '$lid:b$ >> in
 
221
             let t   = <:ctyp< ($tv$,$tv'$) wire -> unit >> in
 
222
             let method_type = Ast.TyPol (_loc, Ast.TyQuo(_loc,b), t) in
 
223
             <:class_str_item< method $lid:"connect_"^a$ : $method_type$ = $lid:a$#connect >> in
 
224
            List.map mill outputs
 
225
          in
 
226
 
 
227
          (* connect method *)
 
228
          let connect =
 
229
           let ivl = fresh_var_names ~blacklist:port_names       ~prefixes:inputs  in
 
230
           let ovl = fresh_var_names ~blacklist:(port_names@ivl) ~prefixes:outputs in
 
231
           let wire_type a b =
 
232
             let va = <:ctyp< '$lid:a$ >> in
 
233
             let vb = <:ctyp< '$lid:b$ >> in
 
234
             <:ctyp< ($va$,$vb$) wire >>
 
235
           in
 
236
           let it = type_of_ctyp_list (List.map2 wire_type ivl inputs) in
 
237
           let ot = type_of_ctyp_list (List.map2 wire_type outputs ovl) in
 
238
           let qivl = List.map (fun x -> Ast.TyQuo(_loc,x)) ivl in
 
239
           let qovl = List.map (fun x -> Ast.TyQuo(_loc,x)) ovl in
 
240
           let pi = pattern_of_string_list inputs  in
 
241
           let po = pattern_of_string_list outputs in
 
242
           let connect_actions = Ast.exSem_of_list (List.map connect_application port_names) in
 
243
           match inputs, outputs with
 
244
           | [] , [] -> assert false
 
245
           |  _ , [] ->
 
246
              let i_forall_vars = List.fold_left (fun x y -> Ast.TyApp (_loc,x,y)) (List.hd qivl) (List.tl qivl) in
 
247
              let method_type = Ast.TyPol (_loc, i_forall_vars, <:ctyp< ($it$ -> unit)>>) in
 
248
              [ <:class_str_item< method connect : $method_type$ = function $pi$ -> begin $connect_actions$ end >> ]
 
249
 
 
250
           | [] , _  ->
 
251
              let o_forall_vars = List.fold_left (fun x y -> Ast.TyApp (_loc,x,y)) (List.hd qovl) (List.tl qovl) in
 
252
              let method_type = Ast.TyPol (_loc, o_forall_vars, <:ctyp< ($ot$ -> unit)>>) in
 
253
              [ <:class_str_item< method connect : $method_type$ = function $po$ -> begin $connect_actions$ end >> ]
 
254
 
 
255
           |  _ , _  ->
 
256
              let qvl = List.append qivl qovl in
 
257
              let forall_vars = List.fold_left (fun x y -> Ast.TyApp (_loc,x,y)) (List.hd qvl) (List.tl qvl) in
 
258
              let method_type = Ast.TyPol (_loc, forall_vars, <:ctyp< ($it$ -> $ot$ -> unit)>>) in
 
259
              [ <:class_str_item< method connect : $method_type$ = function $pi$ -> function $po$ -> begin $connect_actions$ end >> ]
 
260
          in
 
261
 
 
262
          (* disconnect_? methods for all ports *)
 
263
          let disconnect_x =
 
264
            let mill a = <:class_str_item< method $lid:"disconnect_"^a$ = $lid:a$#disconnect >> in
 
265
            List.map mill port_names
 
266
          in
 
267
 
 
268
          (* disconnect method *)
 
269
          let disconnect =
 
270
           let disconnect_actions = Ast.exSem_of_list (List.map disconnect_application port_names)
 
271
            in
 
272
            [ <:class_str_item<
 
273
             method disconnect =
 
274
              begin $disconnect_actions$ end
 
275
            >> ]
 
276
          in
 
277
 
 
278
          let get_wire_connections =
 
279
            let connections_i =
 
280
             let mill a =
 
281
              <:expr< ($str:a$, $lid:a$#connection#map (fun x -> x#readonly_wire#as_common)) >> in
 
282
             List.map mill inputs
 
283
            in
 
284
            let connections_o =
 
285
             let mill a =
 
286
              <:expr< ($str:a$, $lid:a$#connection#map (fun x -> x#writeonly_wire#as_common)) >> in
 
287
             List.map mill outputs
 
288
            in
 
289
            let il = expr_of_expr_list _loc connections_i in
 
290
            let ol = expr_of_expr_list _loc connections_o in
 
291
            [ <:class_str_item< method $lid:"input_wire_connections"$  = $il$ >> ;
 
292
              <:class_str_item< method $lid:"output_wire_connections"$ = $ol$ >> ]
 
293
          in
 
294
 
 
295
          (* set_alone_? methods for output ports *)
 
296
          let set_alone_o =
 
297
            let mill a =
 
298
             <:class_str_item< method private $lid:"set_alone_"^a$ = $lid:a$#connection#extract#writeonly_wire#set_alone >> in
 
299
            List.map mill outputs
 
300
          in
 
301
 
 
302
          (* stabilize method section *)
 
303
          let stabilize =
 
304
            let unchanged_inputs = List.map (fun a -> fresh_var_name ~blacklist:port_names ~prefix:("unchanged_"^a)) inputs in
 
305
 
 
306
            let input_bindings =
 
307
              let mill (a,unchanged_a) =
 
308
                let p = <:patt< ($lid:a$ , $lid:unchanged_a$) >> in
 
309
                <:binding< $p$ = ($lid:a$#read_and_compare) >>
 
310
              in Ast.biAnd_of_list (List.map mill (List.combine inputs unchanged_inputs))
 
311
            in
 
312
 
 
313
            let unchanged_test =
 
314
              let l = (List.map (fun unchanged_a -> <:expr< $lid:unchanged_a$ >>) unchanged_inputs) in
 
315
              List.fold_right (fun e1 e2 -> <:expr< $e1$ && $e2$ >>) l <:expr< true >>
 
316
            in
 
317
 
 
318
            let output_bindings =
 
319
             let inputs_as_lid_expressions = List.map (fun x -> <:expr< $lid:x$>>) inputs            in
 
320
             let firmware_application = apply _loc <:expr< self#firmware>> inputs_as_lid_expressions in
 
321
             let output_pattern = pattern_of_string_list outputs in
 
322
             <:binding< $output_pattern$ = $firmware_application$ >>
 
323
            in
 
324
 
 
325
            let set_actions =
 
326
             Ast.exSem_of_list (List.map set_alone_application outputs)
 
327
            in
 
328
 
 
329
            [ <:class_str_item<
 
330
             method stabilize : performed =
 
331
               let $binding:input_bindings$ in
 
332
                if ($unchanged_test$)
 
333
                then
 
334
                 (Performed false)
 
335
                else
 
336
                 let $binding:output_bindings$ in
 
337
                  $set_actions$ ;
 
338
                 (Performed true)
 
339
            >>]
 
340
 
 
341
          in
 
342
 
 
343
          (* emit method section (for sources) *)
 
344
          let emit =
 
345
 
 
346
            let arg = fresh_var_name ~blacklist:port_names ~prefix:"arg" in
 
347
 
 
348
            let output_bindings =
 
349
             let inputs_as_lid_expressions = List.map (fun x -> <:expr< $lid:x$>>) inputs            in
 
350
             let firmware_application = apply _loc <:expr< self#firmware $lid:arg$ >> inputs_as_lid_expressions in
 
351
             let output_pattern = pattern_of_string_list outputs in
 
352
             <:binding< $output_pattern$ = $firmware_application$ >>
 
353
            in
 
354
 
 
355
            let set_actions = Ast.exSem_of_list (List.map set_alone_application outputs)
 
356
            in
 
357
 
 
358
            [ <:class_str_item<
 
359
             method emit $lid:arg$ =
 
360
                 let actions () =
 
361
                   let $binding:output_bindings$ in
 
362
                   $set_actions$ ;
 
363
                   self#system#stabilize
 
364
                 in self#system#mutex_methods#with_mutex actions
 
365
            >>]
 
366
 
 
367
          in
 
368
 
 
369
          (* Merging sections *)
 
370
          let cst = Ast.crSem_of_list
 
371
           (List.concat
 
372
            [ inherit_section   ;
 
373
              firmware          ;
 
374
              in_port_fields    ;
 
375
              out_port_fields   ;
 
376
              port_names_fields ;
 
377
              connect_i         ;
 
378
              connect_o         ;
 
379
              connect           ;
 
380
              disconnect_x      ;
 
381
              disconnect        ;
 
382
              get_wire_connections;
 
383
              set_alone_o       ;
 
384
              if is_source then emit else stabilize ;
 
385
              user_complement_section ;
 
386
            ])
 
387
           in
 
388
 
 
389
          (* Class expression *)
 
390
 
 
391
          let ce =
 
392
            <:class_expr<
 
393
             fun ?(system : system = (Chip.get_or_initialize_current_system ())) () -> object (self) $cst$ end >> in
 
394
 
 
395
          (* Class expression with optional parameters *)
 
396
          let cop_list = List.map (fun i -> Ast.PaOlb (_loc, i, <:patt< >>)) port_names in
 
397
          let cewop = List.fold_right (fun op ce -> <:class_expr< fun $op$ -> $ce$ >>) cop_list ce in
 
398
          let cewop = <:class_expr< fun ?name ?(equality=(Pervasives.(=))) -> $cewop$ >> in
 
399
          let cewop_with_user_parameters = lambda_class_expr _loc user_parameters cewop in
 
400
          let internal_module_name = ("Chip_class_definition_"^class_name) in
 
401
          let class_def = match is_virtual with
 
402
          | false -> <:str_item< class         [ $ctv$ ] $lid:class_name$ = $cewop_with_user_parameters$ >>
 
403
          | true  -> <:str_item< class virtual [ $ctv$ ] $lid:class_name$ = $cewop_with_user_parameters$ >>
 
404
          in
 
405
          <:str_item<
 
406
 
 
407
            module $uid:internal_module_name$ = struct
 
408
             open Chip
 
409
             $class_def$
 
410
            end
 
411
            include $uid:internal_module_name$
 
412
          >>
 
413
 
 
414
         ] ]
 
415
    ;
 
416
 
 
417
  END
 
418
 
 
419
end
 
420
 
 
421
let module M = Register.OCamlSyntaxExtension (Id) (Make) in ()