check.ml 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. module Id = Type.Id
  2. (** The the Id module, wrap a value in an existencial type with a witness
  3. associate with. *)
  4. type result = R : { value : 'a; witness : 'a Id.t } -> result
  5. let get : type a. a Id.t -> result -> a option =
  6. fun typeid (R { value; witness }) ->
  7. match Id.provably_equal typeid witness with
  8. | Some Type.Equal -> Some value
  9. | None -> None
  10. type t =
  11. | E : {
  12. module_ :
  13. (module S.Analyzer
  14. with type Expression.t = 'a
  15. and type Expression.t' = 'b
  16. and type Instruction.t = 'c
  17. and type Instruction.t' = 'd
  18. and type Location.t = 'e
  19. and type context = 'f);
  20. expr_witness : 'a Id.t;
  21. expr' : 'b Id.t;
  22. instr_witness : 'c Id.t;
  23. instr' : 'd Id.t;
  24. location_witness : 'e Id.t;
  25. context : 'f Id.t;
  26. }
  27. -> t
  28. let build :
  29. (module S.Analyzer
  30. with type Expression.t = _
  31. and type Expression.t' = _
  32. and type Instruction.t = _
  33. and type Instruction.t' = _
  34. and type Location.t = 'a
  35. and type context = _) ->
  36. 'a Id.t * t =
  37. fun module_ ->
  38. let expr_witness = Id.make ()
  39. and expr' = Id.make ()
  40. and instr_witness = Id.make ()
  41. and instr' = Id.make ()
  42. and location_witness = Id.make ()
  43. and context = Id.make () in
  44. let t =
  45. E
  46. {
  47. module_;
  48. expr_witness;
  49. expr';
  50. instr_witness;
  51. instr';
  52. location_witness;
  53. context;
  54. }
  55. in
  56. (location_witness, t)
  57. let get_module : t -> (module S.Analyzer) =
  58. fun (E { module_; _ }) -> (module_ :> (module S.Analyzer))
  59. module type App = sig
  60. val t : t array
  61. end
  62. open StdLabels
  63. module Helper = struct
  64. type 'a expr_list = { witness : 'a Id.t; values : 'a list }
  65. let expr_i : result array list -> 'a Id.t -> int -> 'a expr_list =
  66. fun args witness i ->
  67. let result =
  68. List.fold_left args ~init:{ values = []; witness }
  69. ~f:(fun (type a) ({ values; witness } : a expr_list) t : a expr_list ->
  70. match get witness (Array.get t i) with
  71. | None -> failwith "Does not match"
  72. | Some value_1 -> { values = value_1 :: values; witness })
  73. in
  74. { result with values = result.values }
  75. end
  76. module Make (A : App) = struct
  77. let identifier = "main_checker"
  78. let description = "Internal module"
  79. let is_global = false
  80. let active = ref false
  81. type context = result Array.t
  82. (** We associate each context from the differents test in an array. The
  83. context for this module is a sort of context of contexts *)
  84. (** Initialize each test, and keep the result in the context. *)
  85. let initialize : unit -> context =
  86. fun () ->
  87. Array.map A.t ~f:(fun (E { module_ = (module S); context; _ }) ->
  88. let value = S.initialize () in
  89. R { value; witness = context })
  90. let finalize : result Array.t -> (string * Report.t) list =
  91. fun context_array ->
  92. let _, report =
  93. Array.fold_left A.t ~init:(0, [])
  94. ~f:(fun (i, acc) (E { module_ = (module S); context; _ }) ->
  95. let result = Array.get context_array i in
  96. let local_context = Option.get (get context result) in
  97. let reports = S.finalize local_context in
  98. (i + 1, List.rev_append reports acc))
  99. in
  100. report
  101. (* Global variable for the whole module *)
  102. let len = Array.length A.t
  103. module Expression : S.Expression with type t' = result array = struct
  104. type t = result array
  105. type t' = result array
  106. let literal : S.pos -> t T.literal list -> t =
  107. fun pos values ->
  108. Array.mapi A.t ~f:(fun i (E { module_ = (module S); expr_witness; _ }) ->
  109. (* Map every values to the Checker *)
  110. let values' =
  111. List.map values
  112. ~f:
  113. (T.map_litteral ~f:(fun expr ->
  114. Option.get (get expr_witness (Array.get expr i))))
  115. in
  116. let value = S.Expression.literal pos values' in
  117. R { value; witness = expr_witness })
  118. let integer : S.pos -> string -> t =
  119. fun pos value ->
  120. Array.map A.t ~f:(fun (E { module_ = (module S); expr_witness; _ }) ->
  121. let value = S.Expression.integer pos value in
  122. R { value; witness = expr_witness })
  123. (** Unary operator like [-123] or [+'Text']*)
  124. let uoperator : S.pos -> T.uoperator -> t -> t =
  125. fun pos op values ->
  126. (* Evaluate the nested expression *)
  127. let results = values in
  128. (* Now evaluate the remaining expression.
  129. Traverse both the module the apply, and the matching expression already
  130. evaluated.
  131. It’s easer to use [map] and declare [report] as reference instead of
  132. [fold_left2] and accumulate the report inside the closure, because I
  133. don’t manage the order of the results.
  134. *)
  135. let results =
  136. Array.map2 A.t results
  137. ~f:(fun (E { module_ = (module S); expr_witness; _ }) value ->
  138. match get expr_witness value with
  139. | None -> failwith "Does not match"
  140. | Some value ->
  141. (* Evaluate the single expression *)
  142. let value = S.Expression.uoperator pos op value in
  143. R { witness = expr_witness; value })
  144. in
  145. results
  146. (** Basically the same as uoperator, but operate over two operands instead
  147. of a single one. *)
  148. let boperator : S.pos -> T.boperator -> t -> t -> t =
  149. fun pos op expr1 expr2 ->
  150. Array.init len ~f:(fun i ->
  151. let (E { module_ = (module S); expr_witness; _ }) = Array.get A.t i in
  152. match
  153. ( get expr_witness (Array.get expr1 i),
  154. get expr_witness (Array.get expr2 i) )
  155. with
  156. | Some value_1, Some value_2 ->
  157. let value = S.Expression.boperator pos op value_1 value_2 in
  158. R { witness = expr_witness; value }
  159. | _ -> failwith "Does not match")
  160. (** Call a function. The functions list is hardcoded in lib/lexer.mll *)
  161. let function_ : S.pos -> T.function_ -> t list -> t =
  162. fun pos func args ->
  163. Array.init len ~f:(fun i ->
  164. let (E { module_ = (module S); expr_witness; _ }) = Array.get A.t i in
  165. (* Extract the arguments for each module *)
  166. let args_i = List.rev (Helper.expr_i args expr_witness i).values in
  167. let value = S.Expression.function_ pos func args_i in
  168. R { witness = expr_witness; value })
  169. let ident : (S.pos, t) S.variable -> t =
  170. fun { pos : S.pos; name : string; index : t option } ->
  171. Array.init len ~f:(fun i ->
  172. let (E { module_ = (module S); expr_witness; _ }) = Array.get A.t i in
  173. match index with
  174. | None ->
  175. (* Easest case, just return the plain ident *)
  176. let value = S.Expression.ident { pos; name; index = None } in
  177. R { witness = expr_witness; value }
  178. | Some t -> (
  179. match get expr_witness (Array.get t i) with
  180. | None -> failwith "Does not match"
  181. | Some value_1 ->
  182. let value =
  183. S.Expression.ident { pos; name; index = Some value_1 }
  184. in
  185. R { witness = expr_witness; value }))
  186. (** Convert each internal represention for the expression into its external
  187. representation *)
  188. let v : t -> t' =
  189. fun t ->
  190. let result =
  191. Array.map2 A.t t
  192. ~f:(fun (E { module_ = (module S); expr_witness; expr'; _ }) result ->
  193. match get expr_witness result with
  194. | None -> failwith "Does not match"
  195. | Some value ->
  196. let value = S.Expression.v value in
  197. R { witness = expr'; value })
  198. in
  199. result
  200. end
  201. module Instruction :
  202. S.Instruction
  203. with type expression = Expression.t'
  204. and type t' = result array = struct
  205. type expression = Expression.t'
  206. type t = result array
  207. type t' = result array
  208. let location : S.pos -> string -> t =
  209. fun pos label ->
  210. Array.map A.t ~f:(fun (E { module_ = (module S); instr_witness; _ }) ->
  211. let value = S.Instruction.location pos label in
  212. R { value; witness = instr_witness })
  213. let comment : S.pos -> t =
  214. fun pos ->
  215. Array.map A.t ~f:(fun (E { module_ = (module S); instr_witness; _ }) ->
  216. let value = S.Instruction.comment pos in
  217. R { value; witness = instr_witness })
  218. let expression : expression -> t =
  219. fun expr ->
  220. Array.map2 A.t expr
  221. ~f:(fun (E { module_ = (module S); instr_witness; expr'; _ }) result ->
  222. match get expr' result with
  223. | None -> failwith "Does not match"
  224. | Some value ->
  225. (* The evaluate the instruction *)
  226. let value = S.Instruction.expression value in
  227. R { value; witness = instr_witness })
  228. let call : S.pos -> T.keywords -> expression list -> t =
  229. fun pos keyword args ->
  230. (* The arguments are given like an array of array. Each expression is
  231. actually the list of each expression in the differents modules. *)
  232. Array.init len ~f:(fun i ->
  233. let (E { module_ = (module S); expr'; instr_witness; _ }) =
  234. Array.get A.t i
  235. in
  236. let values = List.rev (Helper.expr_i args expr' i).values in
  237. let value = S.Instruction.call pos keyword values in
  238. R { witness = instr_witness; value })
  239. let act : S.pos -> label:expression -> t list -> t =
  240. fun pos ~label instructions ->
  241. Array.init len ~f:(fun i ->
  242. let (E { module_ = (module S); instr_witness; expr'; _ }) =
  243. Array.get A.t i
  244. in
  245. let values =
  246. List.rev (Helper.expr_i instructions instr_witness i).values
  247. in
  248. match get expr' (Array.get label i) with
  249. | None -> failwith "Does not match"
  250. | Some label_i ->
  251. let value = S.Instruction.act pos ~label:label_i values in
  252. R { witness = instr_witness; value })
  253. (* I think it’s one of the longest module I’ve ever written in OCaml… *)
  254. let assign :
  255. S.pos ->
  256. (S.pos, expression) S.variable ->
  257. T.assignation_operator ->
  258. expression ->
  259. t =
  260. fun pos { pos = var_pos; name; index } op expression ->
  261. Array.init len ~f:(fun i ->
  262. let (E { module_ = (module A); instr_witness; expr'; _ }) =
  263. Array.get A.t i
  264. in
  265. let index_i =
  266. Option.map
  267. (fun expression ->
  268. Option.get (get expr' (Array.get expression i)))
  269. index
  270. in
  271. let variable = S.{ pos = var_pos; name; index = index_i } in
  272. match get expr' (Array.get expression i) with
  273. | None -> failwith "Does not match"
  274. | Some value ->
  275. let value = A.Instruction.assign pos variable op value in
  276. R { value; witness = instr_witness })
  277. let rebuild_clause :
  278. type a b.
  279. int ->
  280. a Id.t ->
  281. b Id.t ->
  282. S.pos * result array * result array list ->
  283. (b, a) S.clause =
  284. fun i instr_witness expr' clause ->
  285. let pos_clause, expr_clause, ts = clause in
  286. match get expr' (Array.get expr_clause i) with
  287. | None -> failwith "Does not match"
  288. | Some value ->
  289. let ts = Helper.expr_i ts instr_witness i in
  290. let ts = List.rev ts.values in
  291. let clause = (pos_clause, value, ts) in
  292. clause
  293. let if_ :
  294. S.pos ->
  295. (expression, t) S.clause ->
  296. elifs:(expression, t) S.clause list ->
  297. else_:(S.pos * t list) option ->
  298. t =
  299. fun pos clause ~elifs ~else_ ->
  300. (* First, apply the report for all the instructions *)
  301. let else_ =
  302. match else_ with
  303. | None -> None
  304. | Some (pos, instructions) -> Some (pos, instructions)
  305. in
  306. Array.init len ~f:(fun i ->
  307. let (E { module_ = (module A); instr_witness; expr'; _ }) =
  308. Array.get A.t i
  309. in
  310. let clause = rebuild_clause i instr_witness expr' clause
  311. and elifs = List.map elifs ~f:(rebuild_clause i instr_witness expr')
  312. and else_ =
  313. match else_ with
  314. | None -> None
  315. | Some (pos, instructions) ->
  316. let elses = Helper.expr_i instructions instr_witness i in
  317. Some (pos, List.rev elses.values)
  318. in
  319. let value = A.Instruction.if_ pos clause ~elifs ~else_ in
  320. R { value; witness = instr_witness })
  321. (** This code is almost a copy/paste from Expression.v but I did not found
  322. a way to factorize it. *)
  323. let v : t -> t' =
  324. fun t ->
  325. let result =
  326. Array.map2 A.t t
  327. ~f:(fun
  328. (E { module_ = (module S); instr_witness; instr'; _ }) result ->
  329. match get instr_witness result with
  330. | None -> failwith "Does not match"
  331. | Some value ->
  332. let value = S.Instruction.v value in
  333. R { witness = instr'; value })
  334. in
  335. result
  336. end
  337. module Location :
  338. S.Location
  339. with type t = result array
  340. and type instruction = Instruction.t'
  341. and type context := context = struct
  342. type instruction = Instruction.t'
  343. type t = result array
  344. let location : context -> S.pos -> instruction list -> t =
  345. fun local_context pos args ->
  346. ignore pos;
  347. let result =
  348. Array.init len ~f:(fun i ->
  349. let (E
  350. { module_ = (module A); instr'; location_witness; context; _ })
  351. =
  352. Array.get A.t i
  353. in
  354. let local_context =
  355. Option.get (get context (Array.get local_context i))
  356. in
  357. let instructions = List.rev (Helper.expr_i args instr' i).values in
  358. let value = A.Location.location local_context pos instructions in
  359. R { value; witness = location_witness })
  360. in
  361. result
  362. let v : t -> Report.t list =
  363. fun args ->
  364. let report = ref [] in
  365. let () =
  366. Array.iteri args ~f:(fun i result ->
  367. let (E { module_ = (module A); location_witness; _ }) =
  368. Array.get A.t i
  369. in
  370. match get location_witness result with
  371. | None -> failwith "Does not match"
  372. | Some value ->
  373. let re = A.Location.v value in
  374. report := List.rev_append re !report)
  375. in
  376. !report
  377. end
  378. end