123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- open StdLabels
- module IgnoreCaseString = struct
- type t = string
- let compare t1 t2 =
- String.compare (String.lowercase_ascii t1) (String.lowercase_ascii t2)
- let equal t1 t2 =
- String.equal (String.lowercase_ascii t1) (String.lowercase_ascii t2)
- end
- module LocationSet = Set.Make (IgnoreCaseString)
- module LocationCalls = Map.Make (IgnoreCaseString)
- let identifier = "locations"
- let description = "Ensure every call points to an existing location"
- let is_global = true
- let active = ref true
- type t = {
- locations : LocationSet.t;
- calls : (string * S.pos) list LocationCalls.t;
- }
- type context = t ref
- let initialize () =
- ref { locations = LocationSet.empty; calls = LocationCalls.empty }
- let finalize : context -> (string * Report.t) list =
- fun context ->
- LocationCalls.fold
- (fun location positions acc ->
- let message = Printf.sprintf "The location %s does not exists" location in
- List.fold_left ~init:acc (List.rev positions)
- ~f:(fun acc (loc, position) ->
- let report = Report.error position message in
- (loc, report) :: acc))
- !context.calls []
- (** Register a new call to a defined location. *)
- let registerCall : S.pos -> string -> t -> t =
- fun pos location t ->
- let file_name = (fst pos).Lexing.pos_fname in
- match
- IgnoreCaseString.equal location file_name
- || LocationSet.mem location t.locations
- with
- | true -> t
- | false ->
- (* The location is not yet defined, register the call for later *)
- let calls =
- LocationCalls.update location
- (function
- | None -> Some [ (file_name, pos) ]
- | Some poss ->
- Some
- (let new_pos = (file_name, pos) in
- new_pos :: poss))
- t.calls
- in
- { t with calls }
- (** Add a new location in the list of all the collected elements *)
- let registerLocation : string -> t -> t =
- fun location t ->
- let calls = LocationCalls.remove location t.calls
- and locations = LocationSet.add location t.locations in
- { calls; locations }
- (** The module Expression is pretty simple, we are only interrested by the
- strings ( because only the first argument of [gt …] is read ).
- If the string is too much complex, we just ignore it. *)
- module Expression = struct
- type t = string option
- include Default.Expression (struct
- type nonrec t = t
- let default = None
- end)
- let v : t -> t' = Fun.id
- (* Extract the litteral if this is a simple text *)
- let literal : S.pos -> t' T.literal list -> t' =
- fun _ ll -> match ll with Text lit :: [] -> Some lit | _ -> None
- end
- module Instruction = struct
- type nonrec t = t -> t
- type t' = t
- let v : t -> t' = Fun.id
- (** Keep a track of every gt or gs instruction *)
- let call : S.pos -> T.keywords -> Expression.t' list -> t =
- fun pos fn args t ->
- match (fn, args) with
- | T.Goto, Some dest :: _ -> registerCall pos dest t
- | T.Gosub, Some dest :: _ -> registerCall pos dest t
- | _ -> t
- let location : S.pos -> string -> t = fun _ _ -> Fun.id
- let comment : S.pos -> t = fun _ -> Fun.id
- let expression : Expression.t' -> t = fun _ -> Fun.id
- let if_ :
- S.pos ->
- (Expression.t', t) S.clause ->
- elifs:(Expression.t', t) S.clause list ->
- else_:(S.pos * t list) option ->
- t =
- fun _ clause ~elifs ~else_ t ->
- let traverse_clause t clause =
- let _, _, block = clause in
- List.fold_left block ~init:t ~f:(fun t instruction -> instruction t)
- in
- let t = traverse_clause t clause in
- let t = List.fold_left ~init:t ~f:traverse_clause elifs in
- match else_ with
- | None -> t
- | Some (_, instructions) ->
- List.fold_left instructions ~init:t ~f:(fun t instruction ->
- instruction t)
- let act : S.pos -> label:Expression.t' -> t list -> t =
- fun _ ~label instructions t ->
- ignore label;
- List.fold_left instructions ~init:t ~f:(fun t instruction -> instruction t)
- let assign :
- S.pos ->
- (S.pos, Expression.t') S.variable ->
- T.assignation_operator ->
- Expression.t' ->
- t =
- fun _ _ _ _ -> Fun.id
- let for_ :
- S.pos ->
- (S.pos, Expression.t') S.variable ->
- start:Expression.t' ->
- to_:Expression.t' ->
- step:Expression.t' option ->
- t list ->
- t =
- fun _ _ ~start ~to_ ~step statements t ->
- ignore start;
- ignore to_;
- ignore step;
- List.fold_left statements ~init:t ~f:(fun t instruction -> instruction t)
- end
- module Location = struct
- type t = unit
- let v : t -> Report.t list = fun () -> []
- let location : context -> S.pos -> Instruction.t list -> t =
- fun context pos instructions ->
- (* Register the location *)
- let file_name = (fst pos).Lexing.pos_fname in
- let c = registerLocation file_name !context in
- (* Then update the list of all the calls to the differents locations *)
- context :=
- List.fold_left instructions ~init:c ~f:(fun t instruction ->
- instruction t)
- end
|