locations.ml 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. open StdLabels
  2. module IgnoreCaseString = struct
  3. type t = string
  4. let compare t1 t2 =
  5. String.compare (String.lowercase_ascii t1) (String.lowercase_ascii t2)
  6. let equal t1 t2 =
  7. String.equal (String.lowercase_ascii t1) (String.lowercase_ascii t2)
  8. end
  9. module LocationSet = Set.Make (IgnoreCaseString)
  10. module LocationCalls = Map.Make (IgnoreCaseString)
  11. let identifier = "locations"
  12. let description = "Ensure every call points to an existing location"
  13. let is_global = true
  14. let active = ref true
  15. type t = {
  16. locations : LocationSet.t;
  17. calls : (string * S.pos) list LocationCalls.t;
  18. }
  19. type context = t ref
  20. let initialize () =
  21. ref { locations = LocationSet.empty; calls = LocationCalls.empty }
  22. let finalize : context -> (string * Report.t) list =
  23. fun context ->
  24. LocationCalls.fold
  25. (fun location positions acc ->
  26. let message = Printf.sprintf "The location %s does not exists" location in
  27. List.fold_left ~init:acc (List.rev positions)
  28. ~f:(fun acc (loc, position) ->
  29. let report = Report.error position message in
  30. (loc, report) :: acc))
  31. !context.calls []
  32. (** Register a new call to a defined location. *)
  33. let registerCall : S.pos -> string -> t -> t =
  34. fun pos location t ->
  35. let file_name = (fst pos).Lexing.pos_fname in
  36. match
  37. IgnoreCaseString.equal location file_name
  38. || LocationSet.mem location t.locations
  39. with
  40. | true -> t
  41. | false ->
  42. (* The location is not yet defined, register the call for later *)
  43. let calls =
  44. LocationCalls.update location
  45. (function
  46. | None -> Some [ (file_name, pos) ]
  47. | Some poss ->
  48. Some
  49. (let new_pos = (file_name, pos) in
  50. new_pos :: poss))
  51. t.calls
  52. in
  53. { t with calls }
  54. (** Add a new location in the list of all the collected elements *)
  55. let registerLocation : string -> t -> t =
  56. fun location t ->
  57. let calls = LocationCalls.remove location t.calls
  58. and locations = LocationSet.add location t.locations in
  59. { calls; locations }
  60. (** The module Expression is pretty simple, we are only interrested by the
  61. strings ( because only the first argument of [gt …] is read ).
  62. If the string is too much complex, we just ignore it. *)
  63. module Expression = struct
  64. type t = string option
  65. include Default.Expression (struct
  66. type nonrec t = t
  67. let default = None
  68. end)
  69. let v : t -> t' = Fun.id
  70. (* Extract the litteral if this is a simple text *)
  71. let literal : S.pos -> t' T.literal list -> t' =
  72. fun _ ll -> match ll with Text lit :: [] -> Some lit | _ -> None
  73. end
  74. module Instruction = struct
  75. type nonrec t = t -> t
  76. type t' = t
  77. let v : t -> t' = Fun.id
  78. (** Keep a track of every gt or gs instruction *)
  79. let call : S.pos -> T.keywords -> Expression.t' list -> t =
  80. fun pos fn args t ->
  81. match (fn, args) with
  82. | T.Goto, Some dest :: _ -> registerCall pos dest t
  83. | T.Gosub, Some dest :: _ -> registerCall pos dest t
  84. | _ -> t
  85. let location : S.pos -> string -> t = fun _ _ -> Fun.id
  86. let comment : S.pos -> t = fun _ -> Fun.id
  87. let expression : Expression.t' -> t = fun _ -> Fun.id
  88. let if_ :
  89. S.pos ->
  90. (Expression.t', t) S.clause ->
  91. elifs:(Expression.t', t) S.clause list ->
  92. else_:(S.pos * t list) option ->
  93. t =
  94. fun _ clause ~elifs ~else_ t ->
  95. let traverse_clause t clause =
  96. let _, _, block = clause in
  97. List.fold_left block ~init:t ~f:(fun t instruction -> instruction t)
  98. in
  99. let t = traverse_clause t clause in
  100. let t = List.fold_left ~init:t ~f:traverse_clause elifs in
  101. match else_ with
  102. | None -> t
  103. | Some (_, instructions) ->
  104. List.fold_left instructions ~init:t ~f:(fun t instruction ->
  105. instruction t)
  106. let act : S.pos -> label:Expression.t' -> t list -> t =
  107. fun _ ~label instructions t ->
  108. ignore label;
  109. List.fold_left instructions ~init:t ~f:(fun t instruction -> instruction t)
  110. let assign :
  111. S.pos ->
  112. (S.pos, Expression.t') S.variable ->
  113. T.assignation_operator ->
  114. Expression.t' ->
  115. t =
  116. fun _ _ _ _ -> Fun.id
  117. let for_ :
  118. S.pos ->
  119. (S.pos, Expression.t') S.variable ->
  120. start:Expression.t' ->
  121. to_:Expression.t' ->
  122. step:Expression.t' option ->
  123. t list ->
  124. t =
  125. fun _ _ ~start ~to_ ~step statements t ->
  126. ignore start;
  127. ignore to_;
  128. ignore step;
  129. List.fold_left statements ~init:t ~f:(fun t instruction -> instruction t)
  130. end
  131. module Location = struct
  132. type t = unit
  133. let v : t -> Report.t list = fun () -> []
  134. let location : context -> S.pos -> Instruction.t list -> t =
  135. fun context pos instructions ->
  136. (* Register the location *)
  137. let file_name = (fst pos).Lexing.pos_fname in
  138. let c = registerLocation file_name !context in
  139. (* Then update the list of all the calls to the differents locations *)
  140. context :=
  141. List.fold_left instructions ~init:c ~f:(fun t instruction ->
  142. instruction t)
  143. end