aboutsummaryrefslogtreecommitdiff
path: root/lib/checks
diff options
context:
space:
mode:
Diffstat (limited to 'lib/checks')
-rw-r--r--lib/checks/check.ml356
-rw-r--r--lib/checks/check.mli8
-rw-r--r--lib/checks/compose.ml130
-rw-r--r--lib/checks/dead_end.ml11
-rw-r--r--lib/checks/dead_end.mli7
-rw-r--r--lib/checks/default.ml47
-rw-r--r--lib/checks/dune1
-rw-r--r--lib/checks/dup_test.ml2
-rw-r--r--lib/checks/dup_test.mli2
-rw-r--r--lib/checks/dynamics.ml19
-rw-r--r--lib/checks/dynamics.mli2
-rw-r--r--lib/checks/get_type.ml291
-rw-r--r--lib/checks/get_type.mli25
-rw-r--r--lib/checks/locations.ml10
-rw-r--r--lib/checks/nested_strings.ml73
-rw-r--r--lib/checks/nested_strings.mli2
-rw-r--r--lib/checks/type_of.ml115
-rw-r--r--lib/checks/type_of.mli10
-rw-r--r--lib/checks/write_only.ml33
19 files changed, 660 insertions, 484 deletions
diff --git a/lib/checks/check.ml b/lib/checks/check.ml
index 6169bb1..597bc0a 100644
--- a/lib/checks/check.ml
+++ b/lib/checks/check.ml
@@ -1,5 +1,4 @@
module S = Qsp_syntax.S
-module C = Qsp_syntax.Catalog
(** The the Id module, wrap a value in an existencial type with a witness
associate with. *)
@@ -11,10 +10,13 @@ let get : type a. a Type.Id.t -> result -> a option =
| Some Type.Equal -> Some value
| None -> None
-type t = Qsp_syntax.Catalog.ex
+let set : type a. a Type.Id.t -> result -> a -> result option =
+ fun typeid (R { witness; _ }) value ->
+ match Type.Id.provably_equal typeid witness with
+ | Some Type.Equal -> Some (R { witness; value })
+ | None -> None
-let get_module : t -> (module S.Analyzer) =
- fun (E { module_; _ }) -> (module_ :> (module S.Analyzer))
+type t = Qsp_syntax.Identifier.t
module type App = sig
val t : t array
@@ -42,23 +44,85 @@ module Make (A : App) = struct
let description = "Internal module"
let is_global = false
let active = ref false
+ let depends = [] (* This modules depends of nothing *)
+
+ type ex = Qsp_syntax.Identifier.t
type context = result Array.t
(** We associate each context from the differents test in an array. The
context for this module is a sort of context of contexts *)
+ (* Initialize the modules to check here, at the module level *)
+ let checks =
+ (* Collect all the dependencies and build the execution order *)
+ let graph : ex list =
+ let rec build_deps l acc =
+ List.fold_left
+ (l : ex list)
+ ~init:acc
+ ~f:(fun
+ acc (Qsp_syntax.Identifier.E { module_ = (module S); _ } as ex) ->
+ let acc' = ex :: acc in
+
+ build_deps S.depends acc')
+ in
+ build_deps (Array.to_list A.t) []
+ in
+
+ (* Convert the dependenciees using the module identifier only, the
+ Tsort.sort function use structural equality comparaison function which
+ does not wok with the module embeded in first class module. *)
+ let graph_name =
+ List.map graph
+ ~f:(fun (Qsp_syntax.Identifier.E { module_ = (module S); _ }) ->
+ let deps' =
+ List.map
+ ~f:(fun (Qsp_syntax.Identifier.E { module_ = (module S); _ }) ->
+ S.identifier)
+ S.depends
+ in
+ (S.identifier, deps'))
+ in
+ match Tsort.sort graph_name with
+ | Tsort.Sorted sorted_graph ->
+ (* From the identifier, extract the associated check *)
+ let _ =
+ List.map sorted_graph ~f:(fun name ->
+ List.find_map graph
+ ~f:(fun
+ (Qsp_syntax.Identifier.E { module_ = (module S); _ } as
+ check)
+ ->
+ match String.equal name S.identifier with
+ | false -> None
+ | true -> Some check)
+ |> Option.get)
+ (* It’s ok to use unchecked option.get here, because the list was
+ created from the same source just before *)
+ in
+
+ Array.of_list graph
+ | Tsort.ErrorCycle _ ->
+ (* This is very unlikely to happen, as it would reflect an error in
+ the compilation units order *)
+ raise Not_found
+
(** Initialize each test, and keep the result in the context. *)
let initialize : unit -> context =
fun () ->
- Array.map A.t ~f:(fun (C.E { module_ = (module S); context; _ }) ->
+ Array.map checks
+ ~f:(fun (Qsp_syntax.Identifier.E { module_ = (module S); context; _ }) ->
let value = S.initialize () in
R { value; witness = context })
let finalize : result Array.t -> (string * Qsp_syntax.Report.t) list =
fun context_array ->
let _, report =
- Array.fold_left A.t ~init:(0, [])
- ~f:(fun (i, acc) (C.E { module_ = (module S); context; _ }) ->
+ Array.fold_left checks ~init:(0, [])
+ ~f:(fun
+ (i, acc)
+ (Qsp_syntax.Identifier.E { module_ = (module S); context; _ })
+ ->
let result = Array.get context_array i in
let local_context = Option.get (get context result) in
let reports = S.finalize local_context in
@@ -67,111 +131,195 @@ module Make (A : App) = struct
report
(* Global variable for the whole module *)
- let len = Array.length A.t
+ let len = Array.length checks
module Expression : S.Expression with type t' = result array = struct
type t = result array
type t' = result array
- let literal : S.pos -> t Qsp_syntax.T.literal list -> t =
- fun pos values ->
- Array.mapi A.t
- ~f:(fun i (C.E { module_ = (module S); expr_witness; _ }) ->
- (* Map every values to the Checker *)
- let values' =
- List.map values
- ~f:
- (Qsp_syntax.T.map_litteral ~f:(fun expr ->
- Option.get (get expr_witness (Array.get expr i))))
- in
- let value = S.Expression.literal pos values' in
- R { value; witness = expr_witness })
-
- let integer : S.pos -> string -> t =
- fun pos value ->
- Array.map A.t ~f:(fun (C.E { module_ = (module S); expr_witness; _ }) ->
- let value = S.Expression.integer pos value in
- R { value; witness = expr_witness })
+ let build_ctx : result option array -> Qsp_syntax.S.extract_context =
+ fun results ->
+ {
+ f =
+ (fun id ->
+ Array.find_map results ~f:(function
+ | Some result -> get id result
+ | None -> None));
+ }
+
+ let literal :
+ ctx:Qsp_syntax.S.extract_context ->
+ S.pos ->
+ t Qsp_syntax.T.literal list ->
+ t =
+ fun ~ctx pos values ->
+ ignore ctx;
+ let results = Array.make len None in
+ (* Create the new array, filled with None at the begining.
+
+ Then populate the array in place in order to read the previous values
+ if requested *)
+ (* Extract the result with the given ID from the array *)
+ let ctx = build_ctx results in
+ let () =
+ Array.mapi_inplace results ~f:(fun i _ ->
+ let (Qsp_syntax.Identifier.E
+ { module_ = (module S); expr_witness; _ }) =
+ Array.get checks i
+ in
+ (* Map every values to the Checker *)
+ let values' =
+ List.map values
+ ~f:
+ (Qsp_syntax.T.map_litteral ~f:(fun expr ->
+ Option.get (get expr_witness (Array.get expr i))))
+ in
+ let value = S.Expression.literal ~ctx pos values' in
+ Some (R { value; witness = expr_witness }))
+ in
+ Array.map results ~f:Option.get
- (** Unary operator like [-123] or [+'Text']*)
- let uoperator : S.pos -> Qsp_syntax.T.uoperator -> t -> t =
- fun pos op values ->
- (* Evaluate the nested expression *)
- let results = values in
+ let integer : ctx:Qsp_syntax.S.extract_context -> S.pos -> string -> t =
+ fun ~ctx pos value ->
+ ignore ctx;
+ let results = Array.make len None in
+ let ctx = build_ctx results in
- (* Now evaluate the remaining expression.
+ let () =
+ Array.mapi_inplace results ~f:(fun i _ ->
+ let (Qsp_syntax.Identifier.E
+ { module_ = (module S); expr_witness; _ }) =
+ Array.get checks i
+ in
+ let value = S.Expression.integer ~ctx pos value in
+ Some (R { value; witness = expr_witness }))
+ in
+ Array.map results ~f:Option.get
- Traverse both the module the apply, and the matching expression already
- evaluated.
+ (** Unary operator like [-123] or [+'Text']*)
+ let uoperator :
+ ctx:Qsp_syntax.S.extract_context ->
+ S.pos ->
+ Qsp_syntax.T.uoperator ->
+ t ->
+ t =
+ fun ~ctx pos op values ->
+ ignore ctx;
+ let results = Array.make len None in
+ let ctx = build_ctx results in
- It’s easer to use [map] and declare [report] as reference instead of
- [fold_left2] and accumulate the report inside the closure, because I
- don’t manage the order of the results.
- *)
- let results =
- Array.map2 A.t results
- ~f:(fun (C.E { module_ = (module S); expr_witness; _ }) value ->
+ (* Evaluate the nested expression *)
+ let () =
+ Array.mapi_inplace results ~f:(fun i _ ->
+ let (Qsp_syntax.Identifier.E
+ { module_ = (module S); expr_witness; _ }) =
+ Array.get checks i
+ in
+ let value = Array.get values i in
match get expr_witness value with
| None -> failwith "Does not match"
| Some value ->
(* Evaluate the single expression *)
- let value = S.Expression.uoperator pos op value in
- R { witness = expr_witness; value })
+ let value = S.Expression.uoperator ~ctx pos op value in
+ Some (R { witness = expr_witness; value }))
in
- results
+ Array.map results ~f:Option.get
(** Basically the same as uoperator, but operate over two operands instead
of a single one. *)
- let boperator : S.pos -> Qsp_syntax.T.boperator -> t -> t -> t =
- fun pos op expr1 expr2 ->
- Array.init len ~f:(fun i ->
- let (E { module_ = (module S); expr_witness; _ }) = Array.get A.t i in
- match
- ( get expr_witness (Array.get expr1 i),
- get expr_witness (Array.get expr2 i) )
- with
- | Some value_1, Some value_2 ->
- let value = S.Expression.boperator pos op value_1 value_2 in
- R { witness = expr_witness; value }
- | _ -> failwith "Does not match")
+ let boperator :
+ ctx:Qsp_syntax.S.extract_context ->
+ S.pos ->
+ Qsp_syntax.T.boperator ->
+ t ->
+ t ->
+ t =
+ fun ~ctx pos op expr1 expr2 ->
+ ignore ctx;
+ let results = Array.make len None in
+ let ctx = build_ctx results in
+ let () =
+ Array.mapi_inplace results ~f:(fun i _ ->
+ let (E { module_ = (module S); expr_witness; _ }) =
+ Array.get checks i
+ in
+ match
+ ( get expr_witness (Array.get expr1 i),
+ get expr_witness (Array.get expr2 i) )
+ with
+ | Some value_1, Some value_2 ->
+ let value =
+ S.Expression.boperator ~ctx pos op value_1 value_2
+ in
+ Some (R { witness = expr_witness; value })
+ | _ -> failwith "Does not match")
+ in
+ Array.map results ~f:Option.get
(** Call a function. The functions list is hardcoded in lib/lexer.mll *)
- let function_ : S.pos -> Qsp_syntax.T.function_ -> t list -> t =
- fun pos func args ->
- Array.init len ~f:(fun i ->
- let (E { module_ = (module S); expr_witness; _ }) = Array.get A.t i in
- (* Extract the arguments for each module *)
- let args_i = List.rev (Helper.expr_i args expr_witness i).values in
- let value = S.Expression.function_ pos func args_i in
- R { witness = expr_witness; value })
-
- let ident : (S.pos, t) S.variable -> t =
- fun { pos : S.pos; name : string; index : t option } ->
- Array.init len ~f:(fun i ->
- let (E { module_ = (module S); expr_witness; _ }) = Array.get A.t i in
-
- match index with
- | None ->
- (* Easest case, just return the plain ident *)
- let value = S.Expression.ident { pos; name; index = None } in
- R { witness = expr_witness; value }
- | Some t -> (
- match get expr_witness (Array.get t i) with
- | None -> failwith "Does not match"
- | Some value_1 ->
- let value =
- S.Expression.ident { pos; name; index = Some value_1 }
- in
- R { witness = expr_witness; value }))
+ let function_ :
+ ctx:Qsp_syntax.S.extract_context ->
+ S.pos ->
+ Qsp_syntax.T.function_ ->
+ t list ->
+ t =
+ fun ~ctx pos func args ->
+ ignore ctx;
+ let results = Array.make len None in
+ let ctx = build_ctx results in
+ let () =
+ Array.mapi_inplace results ~f:(fun i _ ->
+ let (E { module_ = (module S); expr_witness; _ }) =
+ Array.get checks i
+ in
+ (* Extract the arguments for each module *)
+ let args_i = List.rev (Helper.expr_i args expr_witness i).values in
+ let value = S.Expression.function_ ~ctx pos func args_i in
+ Some (R { witness = expr_witness; value }))
+ in
+ Array.map results ~f:Option.get
+
+ let ident : ctx:Qsp_syntax.S.extract_context -> (S.pos, t) S.variable -> t =
+ fun ~ctx { pos : S.pos; name : string; index : t option } ->
+ ignore ctx;
+ let results = Array.make len None in
+ let ctx = build_ctx results in
+ let () =
+ Array.mapi_inplace results ~f:(fun i _ ->
+ let (E { module_ = (module S); expr_witness; _ }) =
+ Array.get checks i
+ in
+
+ match index with
+ | None ->
+ (* Easest case, just return the plain ident *)
+ let value =
+ S.Expression.ident ~ctx { pos; name; index = None }
+ in
+ Some (R { witness = expr_witness; value })
+ | Some t -> (
+ match get expr_witness (Array.get t i) with
+ | None -> failwith "Does not match"
+ | Some value_1 ->
+ let value =
+ S.Expression.ident ~ctx
+ { pos; name; index = Some value_1 }
+ in
+ Some (R { witness = expr_witness; value })))
+ in
+ Array.map results ~f:Option.get
(** Convert each internal represention for the expression into its external
representation *)
let v : t -> t' =
fun t ->
let result =
- Array.map2 A.t t
+ Array.map2 checks t
~f:(fun
- (C.E { module_ = (module S); expr_witness; expr'; _ }) result ->
+ (Qsp_syntax.Identifier.E
+ { module_ = (module S); expr_witness; expr'; _ })
+ result
+ ->
match get expr_witness result with
| None -> failwith "Does not match"
| Some value ->
@@ -191,21 +339,30 @@ module Make (A : App) = struct
let location : S.pos -> string -> t =
fun pos label ->
- Array.map A.t ~f:(fun (C.E { module_ = (module S); instr_witness; _ }) ->
+ Array.map checks
+ ~f:(fun
+ (Qsp_syntax.Identifier.E { module_ = (module S); instr_witness; _ })
+ ->
let value = S.Instruction.location pos label in
R { value; witness = instr_witness })
let comment : S.pos -> t =
fun pos ->
- Array.map A.t ~f:(fun (C.E { module_ = (module S); instr_witness; _ }) ->
+ Array.map checks
+ ~f:(fun
+ (Qsp_syntax.Identifier.E { module_ = (module S); instr_witness; _ })
+ ->
let value = S.Instruction.comment pos in
R { value; witness = instr_witness })
let expression : expression -> t =
fun expr ->
- Array.map2 A.t expr
+ Array.map2 checks expr
~f:(fun
- (C.E { module_ = (module S); instr_witness; expr'; _ }) result ->
+ (Qsp_syntax.Identifier.E
+ { module_ = (module S); instr_witness; expr'; _ })
+ result
+ ->
match get expr' result with
| None -> failwith "Does not match"
| Some value ->
@@ -219,7 +376,7 @@ module Make (A : App) = struct
actually the list of each expression in the differents modules. *)
Array.init len ~f:(fun i ->
let (E { module_ = (module S); expr'; instr_witness; _ }) =
- Array.get A.t i
+ Array.get checks i
in
let values = List.rev (Helper.expr_i args expr' i).values in
@@ -231,7 +388,7 @@ module Make (A : App) = struct
fun pos ~label instructions ->
Array.init len ~f:(fun i ->
let (E { module_ = (module S); instr_witness; expr'; _ }) =
- Array.get A.t i
+ Array.get checks i
in
let values =
List.rev (Helper.expr_i instructions instr_witness i).values
@@ -254,7 +411,7 @@ module Make (A : App) = struct
fun pos { pos = var_pos; name; index } op expression ->
Array.init len ~f:(fun i ->
let (E { module_ = (module A); instr_witness; expr'; _ }) =
- Array.get A.t i
+ Array.get checks i
in
let index_i =
@@ -303,7 +460,7 @@ module Make (A : App) = struct
in
Array.init len ~f:(fun i ->
let (E { module_ = (module A); instr_witness; expr'; _ }) =
- Array.get A.t i
+ Array.get checks i
in
let clause = rebuild_clause i instr_witness expr' clause
@@ -324,9 +481,12 @@ module Make (A : App) = struct
let v : t -> t' =
fun t ->
let result =
- Array.map2 A.t t
+ Array.map2 checks t
~f:(fun
- (C.E { module_ = (module S); instr_witness; instr'; _ }) result ->
+ (Qsp_syntax.Identifier.E
+ { module_ = (module S); instr_witness; instr'; _ })
+ result
+ ->
match get instr_witness result with
| None -> failwith "Does not match"
| Some value ->
@@ -358,7 +518,7 @@ module Make (A : App) = struct
context;
_;
}) =
- Array.get A.t i
+ Array.get checks i
in
let local_context =
@@ -377,7 +537,7 @@ module Make (A : App) = struct
let () =
Array.iteri args ~f:(fun i result ->
let (E { module_ = (module A); location_witness; _ }) =
- Array.get A.t i
+ Array.get checks i
in
match get location_witness result with
| None -> failwith "Does not match"
diff --git a/lib/checks/check.mli b/lib/checks/check.mli
index ebed0df..34d953f 100644
--- a/lib/checks/check.mli
+++ b/lib/checks/check.mli
@@ -13,19 +13,19 @@
end)
]} *)
-val get_module : Qsp_syntax.Catalog.ex -> (module Qsp_syntax.S.Analyzer)
-
type result
val get : 'a Type.Id.t -> result -> 'a option
(** The method [get] can be used to get the internal value for one of the
checker used. *)
+val set : 'a Type.Id.t -> result -> 'a -> result option
+
module Make (A : sig
- val t : Qsp_syntax.Catalog.ex array
+ val t : Qsp_syntax.Identifier.t array
end) : sig
include
- Qsp_syntax.S.Analyzer
+ Qsp_syntax.Analyzer.T
with type Location.t = result array
and type context = result array
end
diff --git a/lib/checks/compose.ml b/lib/checks/compose.ml
deleted file mode 100644
index b29c22e..0000000
--- a/lib/checks/compose.ml
+++ /dev/null
@@ -1,130 +0,0 @@
-(** Build a module with the result from another one module *)
-
-open StdLabels
-module S = Qsp_syntax.S
-module T = Qsp_syntax.T
-
-(** Make a module lazy *)
-module Lazier (E : S.Expression) :
- S.Expression with type t' = E.t' Lazy.t and type t = E.t Lazy.t = struct
- type t = E.t Lazy.t
- type t' = E.t' Lazy.t
-
- let v : E.t Lazy.t -> E.t' Lazy.t = Lazy.map E.v
- let integer : S.pos -> string -> t = fun pos i -> lazy (E.integer pos i)
-
- let ident : (S.pos, t) S.variable -> t =
- fun { pos; name : string; index : t option } ->
- lazy (E.ident { pos; name; index = Option.map Lazy.force index })
-
- let literal : S.pos -> t T.literal list -> t =
- fun pos litts ->
- lazy
- (let e_litts = List.map litts ~f:(T.map_litteral ~f:Lazy.force) in
- E.literal pos e_litts)
-
- let function_ : S.pos -> T.function_ -> t list -> t =
- fun pos f e ->
- lazy
- (let e' = List.map ~f:Lazy.force e in
- E.function_ pos f e')
-
- let uoperator : S.pos -> T.uoperator -> t -> t =
- fun pos op t ->
- let t' = lazy (E.uoperator pos op (Lazy.force t)) in
- t'
-
- let boperator : S.pos -> T.boperator -> t -> t -> t =
- fun pos op t1 t2 ->
- let t' = lazy (E.boperator pos op (Lazy.force t1) (Lazy.force t2)) in
- t'
-end
-
-(** Build an expression module with the result from another expression. The
- signature of the fuctions is a bit different, as they all receive the result
- from the previous evaluated element in argument. *)
-module Expression (E : S.Expression) = struct
- module type SIG = sig
- type t
- type t'
-
- (* Override the type [t] in the definition of all the functions. The
- signatures differs a bit from the standard signature as they get the
- result from E.t in last argument *)
-
- val ident : (S.pos, E.t' Lazy.t * t) S.variable -> E.t' Lazy.t -> t
- val integer : S.pos -> string -> E.t' Lazy.t -> t
- val literal : S.pos -> (E.t' Lazy.t * t) T.literal list -> E.t' Lazy.t -> t
-
- val function_ :
- S.pos -> T.function_ -> (E.t' Lazy.t * t) list -> E.t' Lazy.t -> t
-
- val uoperator : S.pos -> T.uoperator -> E.t' Lazy.t * t -> E.t' Lazy.t -> t
-
- val boperator :
- S.pos ->
- T.boperator ->
- E.t' Lazy.t * t ->
- E.t' Lazy.t * t ->
- E.t' Lazy.t ->
- t
-
- val v : E.t' Lazy.t * t -> t'
- (** Convert from the internal representation to the external one. *)
- end
-
- (* Create a lazy version of the module *)
- module E = Lazier (E)
-
- module Make (M : SIG) : S.Expression with type t' = M.t' = struct
- type t = E.t * M.t
- type t' = M.t'
-
- let v' : E.t -> E.t' = E.v
- let v : t -> t' = fun (type_of, v) -> M.v (v' type_of, v)
-
- let ident : (S.pos, t) S.variable -> t =
- fun { pos; name : string; index : t option } ->
- let t' = E.ident { pos; name; index = Option.map fst index } in
- let index' = Option.map (fun (e, m) -> (v' e, m)) index in
- (t', M.ident { pos; name; index = index' } (v' t'))
-
- let integer : S.pos -> string -> t =
- fun pos i ->
- let t' = E.integer pos i in
- (t', M.integer pos i (v' t'))
-
- let literal : S.pos -> t T.literal list -> t =
- fun pos litts ->
- let litts' =
- List.map litts ~f:(T.map_litteral ~f:(fun (e, m) -> (v' e, m)))
- in
-
- let t' =
- let e_litts = List.map litts ~f:(T.map_litteral ~f:fst) in
- E.literal pos e_litts
- in
- (t', M.literal pos litts' (v' t'))
-
- let function_ : S.pos -> T.function_ -> t list -> t =
- fun pos f expressions ->
- let e = List.map ~f:fst expressions
- and expressions' = List.map ~f:(fun (e, m) -> (v' e, m)) expressions in
-
- let t' = E.function_ pos f e in
- (t', M.function_ pos f expressions' (v' t'))
-
- let uoperator : S.pos -> T.uoperator -> t -> t =
- fun pos op (t, expr) ->
- let t' = E.uoperator pos op t in
- (t', M.uoperator pos op (v' t, expr) (v' t'))
-
- let boperator : S.pos -> T.boperator -> t -> t -> t =
- fun pos op (t1, expr1) (t2, expr2) ->
- let t' = E.boperator pos op t1 t2 in
- (t', M.boperator pos op (v' t1, expr1) (v' t2, expr2) (v' t'))
- end
-end
-
-module TypeBuilder = Expression (Get_type)
-(** Builder adding the type for the expression *)
diff --git a/lib/checks/dead_end.ml b/lib/checks/dead_end.ml
index 629a966..dd3e945 100644
--- a/lib/checks/dead_end.ml
+++ b/lib/checks/dead_end.ml
@@ -7,7 +7,9 @@ let identifier = "dead_end"
let description = "Check for dead end in the code"
let is_global = false
let active = ref false
+let depends = []
+type ex = Qsp_syntax.Identifier.t
type context = unit
let initialize = Fun.id
@@ -40,10 +42,8 @@ module Instruction = struct
(** For each instruction, return thoses two informations :
- - the intruction contains at [gt]
- - the last instruction is a [gt]
-
- *)
+ - the intruction contains at [gt]
+ - the last instruction is a [gt] *)
let v : t -> t' = fun t -> t
let default =
@@ -73,7 +73,8 @@ module Instruction = struct
(** Raw expression *)
let expression : Expression.t' -> t = fun _ -> default
- (** The content of a block is very linear, I only need to check the last element *)
+ (** The content of a block is very linear, I only need to check the last
+ element *)
let check_block : S.pos -> t list -> t =
fun pos instructions ->
let last_element =
diff --git a/lib/checks/dead_end.mli b/lib/checks/dead_end.mli
index d8fe7d6..73ec86a 100644
--- a/lib/checks/dead_end.mli
+++ b/lib/checks/dead_end.mli
@@ -1,6 +1,5 @@
-(** Checker looking for the dead ends in the source.
+(** Checker looking for the dead ends in the source.
- A dead end is a state where the user does not have any action.
- *)
+ A dead end is a state where the user does not have any action. *)
-include Qsp_syntax.S.Analyzer
+include Qsp_syntax.Analyzer.T with type ex = Qsp_syntax.Identifier.t
diff --git a/lib/checks/default.ml b/lib/checks/default.ml
index 0c4d761..0ec1084 100644
--- a/lib/checks/default.ml
+++ b/lib/checks/default.ml
@@ -21,25 +21,56 @@ struct
type t' = T'.t
- let ident : (S.pos, T'.t) S.variable -> T'.t = fun _ -> T'.default
+ let ident :
+ ctx:Qsp_syntax.S.extract_context -> (S.pos, T'.t) S.variable -> T'.t =
+ fun ~ctx _ ->
+ ignore ctx;
+ T'.default
(*
Basic values, text, number…
*)
- let integer : S.pos -> string -> T'.t = fun _ _ -> T'.default
- let literal : S.pos -> T'.t T.literal list -> T'.t = fun _ _ -> T'.default
+ let integer : ctx:Qsp_syntax.S.extract_context -> S.pos -> string -> T'.t =
+ fun ~ctx _ _ ->
+ ignore ctx;
+ T'.default
+
+ let literal :
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> T'.t T.literal list -> T'.t =
+ fun ~ctx _ _ ->
+ ignore ctx;
+ T'.default
(** Call a function. The functions list is hardcoded in lib/lexer.mll *)
- let function_ : S.pos -> T.function_ -> T'.t list -> T'.t =
- fun _ _ _ -> T'.default
+ let function_ :
+ ctx:Qsp_syntax.S.extract_context ->
+ S.pos ->
+ T.function_ ->
+ T'.t list ->
+ T'.t =
+ fun ~ctx _ _ _ ->
+ ignore ctx;
+ T'.default
(** Unary operator like [-123] or [+'Text']*)
- let uoperator : S.pos -> T.uoperator -> T'.t -> T'.t = fun _ _ _ -> T'.default
+ let uoperator :
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> T.uoperator -> T'.t -> T'.t =
+ fun ~ctx _ _ _ ->
+ ignore ctx;
+ T'.default
(** Binary operator, for a comparaison, or an operation *)
- let boperator : S.pos -> T.boperator -> T'.t -> T'.t -> T'.t =
- fun _ _ _ _ -> T'.default
+ let boperator :
+ ctx:Qsp_syntax.S.extract_context ->
+ S.pos ->
+ T.boperator ->
+ T'.t ->
+ T'.t ->
+ T'.t =
+ fun ~ctx _ _ _ _ ->
+ ignore ctx;
+ T'.default
end
module Instruction (Expression : sig
diff --git a/lib/checks/dune b/lib/checks/dune
index 3bd22e0..75b311b 100644
--- a/lib/checks/dune
+++ b/lib/checks/dune
@@ -1,6 +1,7 @@
(library
(name qsp_checks)
(libraries
+ tsort
qsp_syntax
)
diff --git a/lib/checks/dup_test.ml b/lib/checks/dup_test.ml
index c29eca9..4de9a4d 100644
--- a/lib/checks/dup_test.ml
+++ b/lib/checks/dup_test.ml
@@ -13,7 +13,9 @@ let identifier = "duplicate_test"
let description = "Check for duplicate tests"
let is_global = false
let active = ref true
+let depends = []
+type ex = Qsp_syntax.Identifier.t
type context = unit
let initialize = Fun.id
diff --git a/lib/checks/dup_test.mli b/lib/checks/dup_test.mli
index 6446c67..a771a46 100644
--- a/lib/checks/dup_test.mli
+++ b/lib/checks/dup_test.mli
@@ -1 +1 @@
-include Qsp_syntax.S.Analyzer
+include Qsp_syntax.Analyzer.T with type ex = Qsp_syntax.Identifier.t
diff --git a/lib/checks/dynamics.ml b/lib/checks/dynamics.ml
index 0c16ff8..f88550b 100644
--- a/lib/checks/dynamics.ml
+++ b/lib/checks/dynamics.ml
@@ -7,7 +7,9 @@ let identifier = "dynamics"
let description = "Report all dynamics string in the module"
let is_global = true
let active = ref false
+let depends = []
+type ex = Qsp_syntax.Identifier.t
type text = { content : string; position : S.pos } [@@deriving eq, ord]
module StringSet = Set.Make (struct
@@ -54,8 +56,10 @@ module Expression = struct
let v : t -> t' = Fun.id
(** Only keep the raw strings *)
- let literal : S.pos -> t T.literal list -> t =
- fun position content ->
+ let literal :
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> t T.literal list -> t =
+ fun ~ctx position content ->
+ ignore ctx;
ignore position;
match content with
| [ T.Text content ] -> Text { content; position }
@@ -91,13 +95,16 @@ module Expression = struct
(** Consider the integer as text. This is easier for evaluating the indices in
the arrays (it use the same code as text indices), and will report bad use
of dynamics. *)
- let integer : S.pos -> string -> t =
- fun position content -> Text { content; position }
+ let integer : ctx:Qsp_syntax.S.extract_context -> S.pos -> string -> t =
+ fun ~ctx position content ->
+ ignore ctx;
+ Text { content; position }
(** If the identifier uses any unmanaged expression in the indices, ignore it.
*)
- let ident : (S.pos, t) S.variable -> t =
- fun ({ index; _ } as ident) ->
+ let ident : ctx:Qsp_syntax.S.extract_context -> (S.pos, t) S.variable -> t =
+ fun ~ctx ({ index; _ } as ident) ->
+ ignore ctx;
let is_valid =
Option.fold ~none:true index ~some:(fun opt ->
match opt with None -> false | _ -> true)
diff --git a/lib/checks/dynamics.mli b/lib/checks/dynamics.mli
index b4cdc96..588a05e 100644
--- a/lib/checks/dynamics.mli
+++ b/lib/checks/dynamics.mli
@@ -1,4 +1,4 @@
-include Qsp_syntax.S.Analyzer
+include Qsp_syntax.Analyzer.T with type ex = Qsp_syntax.Identifier.t
type text = { content : string; position : Qsp_syntax.S.pos }
diff --git a/lib/checks/get_type.ml b/lib/checks/get_type.ml
index 2486afa..00270c2 100644
--- a/lib/checks/get_type.ml
+++ b/lib/checks/get_type.ml
@@ -17,116 +17,183 @@ type type_of =
(** String containing a numeric value *)
[@@deriving show { with_path = false }, eq]
-type t = Variable of type_of | Raw of type_of [@@deriving show, eq]
-type t' = t
-
-let v = Fun.id
-let get_type : t -> type_of = function Raw r -> r | Variable r -> r
-
-let map : t -> type_of -> t =
- fun t type_of ->
- match t with Raw _ -> Raw type_of | Variable _ -> Variable type_of
-
-let get_nature : t -> t -> type_of -> t =
- fun t1 t2 type_of ->
- match (t1, t2) with
- | Variable _, _ -> Variable type_of
- | _, Variable _ -> Variable type_of
- | Raw _, Raw _ -> Raw type_of
-
-let integer : S.pos -> string -> t = fun _ _ -> Raw Integer
-
-let ident : (S.pos, 'any) S.variable -> t =
- fun var ->
- match var.name.[0] with '$' -> Variable String | _ -> Variable Integer
-
-let literal : S.pos -> t T.literal list -> t =
- fun pos values ->
- ignore pos;
- let init = None in
- let typed =
- List.fold_left values ~init ~f:(fun state -> function
- | T.Text t -> (
- (* Tranform the type, but keep the information is it’s a raw data
+module Expression = struct
+ type t = Variable of type_of | Raw of type_of [@@deriving show, eq]
+ type t' = t
+
+ let v = Fun.id
+ let get_type : t -> type_of = function Raw r -> r | Variable r -> r
+
+ let map : t -> type_of -> t =
+ fun t type_of ->
+ match t with Raw _ -> Raw type_of | Variable _ -> Variable type_of
+
+ let get_nature : t -> t -> type_of -> t =
+ fun t1 t2 type_of ->
+ match (t1, t2) with
+ | Variable _, _ -> Variable type_of
+ | _, Variable _ -> Variable type_of
+ | Raw _, Raw _ -> Raw type_of
+
+ let integer : ctx:Qsp_syntax.S.extract_context -> S.pos -> string -> t =
+ fun ~ctx _ _ ->
+ ignore ctx;
+ Raw Integer
+
+ let ident : ctx:Qsp_syntax.S.extract_context -> (S.pos, 'any) S.variable -> t
+ =
+ fun ~ctx var ->
+ ignore ctx;
+ match var.name.[0] with '$' -> Variable String | _ -> Variable Integer
+
+ let literal :
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> t T.literal list -> t =
+ fun ~ctx pos values ->
+ ignore ctx;
+ ignore pos;
+ let init = None in
+ let typed =
+ List.fold_left values ~init ~f:(fun state -> function
+ | T.Text t -> (
+ (* Tranform the type, but keep the information is it’s a raw data
or a variable one *)
- let nature = Option.value ~default:(Raw Integer) state in
- match (Option.map get_type state, int_of_string_opt t) with
- | None, Some _
- | Some Integer, Some _
- | Some NumericString, Some _
- | Some Bool, Some _ ->
- Some (map nature NumericString)
- | _, _ ->
- if String.equal "" t then
- (* If the text is empty, ignore it *)
- state
- else Some (map nature String))
- | T.Expression t -> (
- let nature = Option.value ~default:(Raw Integer) state in
- match (Option.map get_type state, get_type t) with
- | None, Integer | Some NumericString, Integer ->
- Some (get_nature nature t NumericString)
- | _ -> Some (map nature String)))
- in
- let result = Option.value ~default:(Raw String) typed in
- result
-
-let uoperator : S.pos -> T.uoperator -> t -> t =
- fun pos operator t ->
- ignore pos;
- match operator with Add -> t | Neg | No -> Raw Integer
-
-let boperator : S.pos -> T.boperator -> t -> t -> t =
- fun pos operator t1 t2 ->
- ignore pos;
- match operator with
- | T.Plus -> (
- match (get_type t1, get_type t2) with
- | Integer, Integer -> get_nature t1 t2 Integer
- | String, _ -> get_nature t1 t2 String
- | _, String -> get_nature t1 t2 String
- | (_ as t), Bool -> get_nature t1 t2 t
- | Bool, (_ as t) -> get_nature t1 t2 t
- | (_ as t), NumericString -> get_nature t1 t2 t
- | NumericString, (_ as t) -> get_nature t1 t2 t)
- | T.Eq | T.Neq -> get_nature t1 t2 Bool
- | T.Mod | T.Minus | T.Product | T.Div -> get_nature t1 t2 Integer
- | T.And | T.Or -> get_nature t1 t2 Bool
- | Lt | Gte | Lte | Gt -> get_nature t1 t2 Bool
-
-let function_ : S.pos -> T.function_ -> t list -> t =
- fun pos function_ params ->
- ignore pos;
- match function_ with
- | Dyneval | Dyneval' -> Variable NumericString
- | Arrcomp | Arrpos | Arrsize | Countobj | Desc | Getobj | Instr | Isplay ->
- Variable Integer
- | Desc' | Getobj' -> Variable String
- | Func | Func' -> Variable NumericString
- | Iif | Iif' -> ( match params with _ :: t :: _ -> t | _ -> Raw Bool)
- | Input | Input' -> Variable NumericString
- | Isnum -> Raw Bool
- | Lcase | Lcase' | Ucase | Ucase' -> Raw String
- | Len -> Variable Integer
- | Loc -> Variable Bool
- | Max | Max' | Min | Min' -> (
- match params with
- | [] -> Raw Bool
- | Raw String :: [] | Variable String :: [] -> Variable NumericString
- | hd :: _ -> hd)
- | Mid | Mid' -> Variable String
- | Msecscount -> Variable Integer
- | Rand -> Variable Integer
- | Replace -> Variable String
- | Replace' -> Variable String
- | Rgb -> Variable Integer
- | Rnd -> Variable Integer
- | Selact -> Variable String
- | Str | Str' -> Raw String
- | Strcomp -> Raw Bool
- | Strfind -> Variable String
- | Strfind' -> Variable String
- | Strpos -> Variable Integer
- | Trim -> Variable String
- | Trim' -> Variable String
- | Val -> Variable Integer
+ let nature = Option.value ~default:(Raw Integer) state in
+ match (Option.map get_type state, int_of_string_opt t) with
+ | None, Some _
+ | Some Integer, Some _
+ | Some NumericString, Some _
+ | Some Bool, Some _ ->
+ Some (map nature NumericString)
+ | _, _ ->
+ if String.equal "" t then
+ (* If the text is empty, ignore it *)
+ state
+ else Some (map nature String))
+ | T.Expression t -> (
+ let nature = Option.value ~default:(Raw Integer) state in
+ match (Option.map get_type state, get_type t) with
+ | None, Integer | Some NumericString, Integer ->
+ Some (get_nature nature t NumericString)
+ | _ -> Some (map nature String)))
+ in
+ let result = Option.value ~default:(Raw String) typed in
+ result
+
+ let uoperator :
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> T.uoperator -> t -> t =
+ fun ~ctx pos operator t ->
+ ignore ctx;
+ ignore pos;
+ match operator with Add -> t | Neg | No -> Raw Integer
+
+ let boperator :
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> T.boperator -> t -> t -> t =
+ fun ~ctx pos operator t1 t2 ->
+ ignore ctx;
+ ignore pos;
+ match operator with
+ | T.Plus -> (
+ match (get_type t1, get_type t2) with
+ | Integer, Integer -> get_nature t1 t2 Integer
+ | String, _ -> get_nature t1 t2 String
+ | _, String -> get_nature t1 t2 String
+ | (_ as t), Bool -> get_nature t1 t2 t
+ | Bool, (_ as t) -> get_nature t1 t2 t
+ | (_ as t), NumericString -> get_nature t1 t2 t
+ | NumericString, (_ as t) -> get_nature t1 t2 t)
+ | T.Eq | T.Neq -> get_nature t1 t2 Bool
+ | T.Mod | T.Minus | T.Product | T.Div -> get_nature t1 t2 Integer
+ | T.And | T.Or -> get_nature t1 t2 Bool
+ | Lt | Gte | Lte | Gt -> get_nature t1 t2 Bool
+
+ let function_ :
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> T.function_ -> t list -> t =
+ fun ~ctx pos function_ params ->
+ ignore ctx;
+ ignore pos;
+ match function_ with
+ | Dyneval | Dyneval' -> Variable NumericString
+ | Arrcomp | Arrpos | Arrsize | Countobj | Desc | Getobj | Instr | Isplay ->
+ Variable Integer
+ | Desc' | Getobj' -> Variable String
+ | Func | Func' -> Variable NumericString
+ | Iif | Iif' -> ( match params with _ :: t :: _ -> t | _ -> Raw Bool)
+ | Input | Input' -> Variable NumericString
+ | Isnum -> Raw Bool
+ | Lcase | Lcase' | Ucase | Ucase' -> Raw String
+ | Len -> Variable Integer
+ | Loc -> Variable Bool
+ | Max | Max' | Min | Min' -> (
+ match params with
+ | [] -> Raw Bool
+ | Raw String :: [] | Variable String :: [] -> Variable NumericString
+ | hd :: _ -> hd)
+ | Mid | Mid' -> Variable String
+ | Msecscount -> Variable Integer
+ | Rand -> Variable Integer
+ | Replace -> Variable String
+ | Replace' -> Variable String
+ | Rgb -> Variable Integer
+ | Rnd -> Variable Integer
+ | Selact -> Variable String
+ | Str | Str' -> Raw String
+ | Strcomp -> Raw Bool
+ | Strfind -> Variable String
+ | Strfind' -> Variable String
+ | Strpos -> Variable Integer
+ | Trim -> Variable String
+ | Trim' -> Variable String
+ | Val -> Variable Integer
+end
+
+module A = struct
+ let identifier = "get_types"
+ let description = "Identify the type for an expression"
+ let is_global = true
+ let active = ref false
+ let depends = []
+
+ type ex = Qsp_syntax.Identifier.t
+ type context = unit
+
+ let initialize () = ()
+
+ module Expression = Expression
+
+ module Instruction = struct
+ type t = unit
+ type t' = unit
+
+ include
+ Default.Instruction
+ (Expression)
+ (struct
+ type t = unit
+
+ let default = ()
+ let fold seq = Seq.iter (fun _ -> ()) seq
+ end)
+
+ let v = Fun.id
+ end
+
+ module Location = struct
+ type t = unit
+ type instruction = Instruction.t'
+
+ let location : context -> S.pos -> instruction list -> t =
+ fun context pos instr ->
+ ignore context;
+ ignore pos;
+ List.iter instr ~f:(fun _ -> ())
+
+ let v : t -> Report.t list = fun _ -> []
+ end
+
+ let finalize context =
+ ignore context;
+ []
+end
+
+let expression_id = Type.Id.make ()
+let ex = Qsp_syntax.Identifier.build ~expression_id (module A)
diff --git a/lib/checks/get_type.mli b/lib/checks/get_type.mli
new file mode 100644
index 0000000..476059b
--- /dev/null
+++ b/lib/checks/get_type.mli
@@ -0,0 +1,25 @@
+type type_of =
+ | Integer (** A numeric value *)
+ | Bool (** A boolean, not a real type *)
+ | String (** String value *)
+ | NumericString (** String containing a numeric value *)
+[@@deriving show, eq]
+
+module Expression : sig
+ type t = Variable of type_of | Raw of type_of [@@deriving show, eq]
+ type t' = t
+
+ include Qsp_syntax.S.Expression with type t := t and type t' := t'
+
+ val ident :
+ ctx:Qsp_syntax.S.extract_context ->
+ (Qsp_syntax.S.pos, 'any) Qsp_syntax.S.variable ->
+ t
+
+ val get_type : t -> type_of
+end
+
+val expression_id : Expression.t Type.Id.t
+(** Type identifier for the expression in this module *)
+
+val ex : Qsp_syntax.Identifier.t
diff --git a/lib/checks/locations.ml b/lib/checks/locations.ml
index 8e5f500..3a5ddf5 100644
--- a/lib/checks/locations.ml
+++ b/lib/checks/locations.ml
@@ -20,6 +20,9 @@ let identifier = "locations"
let description = "Ensure every call points to an existing location"
let is_global = true
let active = ref true
+let depends = []
+
+type ex = Qsp_syntax.Identifier.t
type t = {
locations : LocationSet.t;
@@ -89,8 +92,11 @@ module Expression = struct
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
+ let literal :
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> t' T.literal list -> t' =
+ fun ~ctx _ ll ->
+ ignore ctx;
+ match ll with Text lit :: [] -> Some lit | _ -> None
end
module Instruction = struct
diff --git a/lib/checks/nested_strings.ml b/lib/checks/nested_strings.ml
index 51c5258..d4a7947 100644
--- a/lib/checks/nested_strings.ml
+++ b/lib/checks/nested_strings.ml
@@ -7,80 +7,77 @@ let identifier = "escaped_string"
let description = "Check for unnecessary use of expression encoded in string"
let is_global = false
let active = ref true
+let depends = [ Get_type.ex ]
+type ex = Qsp_syntax.Identifier.t
type context = unit
let initialize = Fun.id
let finalize () = []
-module Expression = Compose.TypeBuilder.Make (struct
- type t = Report.t list
+module Expression = struct
+ type t = { type_of : Get_type.Expression.t; report : Report.t list }
type t' = Report.t list
- let v : Get_type.t Lazy.t * t -> t' = snd
+ let v : t -> t' = fun t -> t.report
(** Identify the expressions reprented as string. That’s here that the report
are added.
All the rest of the module only push thoses warning to the top level. *)
let literal :
- S.pos -> (Get_type.t Lazy.t * t) T.literal list -> Get_type.t Lazy.t -> t
- =
- fun pos content _type_of ->
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> t T.literal list -> t =
+ fun ~ctx pos content ->
+ let type_of = Option.get (ctx.f Get_type.expression_id) in
match content with
- | [ T.Expression (t', _); T.Text "" ] -> (
- match Get_type.get_type (Lazy.force t') with
- | Get_type.Integer -> []
+ | [ T.Expression t; T.Text "" ] -> (
+ match Get_type.Expression.get_type t.type_of with
+ | Get_type.Integer -> { type_of; report = [] }
| _ ->
let msg = Report.debug pos "This expression can be simplified" in
- [ msg ])
- | _ -> []
+ { type_of; report = [ msg ] })
+ | _ -> { type_of; report = [] }
- let ident :
- (S.pos, Get_type.t Lazy.t * t) S.variable -> Get_type.t Lazy.t -> t =
- fun variable _type_of ->
- match variable.index with None -> [] | Some (_, t) -> t
+ let ident : ctx:Qsp_syntax.S.extract_context -> (S.pos, t) S.variable -> t =
+ fun ~ctx variable ->
+ let type_of = Option.get (ctx.f Get_type.expression_id) in
+ match variable.index with None -> { type_of; report = [] } | Some t -> t
- let integer : S.pos -> string -> Get_type.t Lazy.t -> t =
- fun pos t _type_of ->
+ let integer : ctx:Qsp_syntax.S.extract_context -> S.pos -> string -> t =
+ fun ~ctx pos t ->
ignore pos;
ignore t;
- []
+ let type_of = Option.get (ctx.f Get_type.expression_id) in
+ { type_of; report = [] }
let function_ :
- S.pos ->
- T.function_ ->
- (Get_type.t Lazy.t * t) list ->
- Get_type.t Lazy.t ->
- t =
- fun pos f expressions _type_of ->
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> T.function_ -> t list -> t =
+ fun ~ctx pos f expressions ->
+ let type_of = Option.get (ctx.f Get_type.expression_id) in
ignore pos;
ignore f;
let exprs =
List.fold_left ~init:[] expressions ~f:(fun acc el ->
- List.rev_append (snd el) acc)
+ List.rev_append el.report acc)
in
- exprs
+ { type_of; report = exprs }
let uoperator :
- S.pos -> T.uoperator -> Get_type.t Lazy.t * t -> Get_type.t Lazy.t -> t =
- fun pos op r _type_of ->
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> T.uoperator -> t -> t =
+ fun ~ctx pos op r ->
+ let type_of = Option.get (ctx.f Get_type.expression_id) in
ignore op;
ignore pos;
- snd r
+ { r with type_of }
let boperator :
- S.pos ->
- T.boperator ->
- Get_type.t Lazy.t * t ->
- Get_type.t Lazy.t * t ->
- Get_type.t Lazy.t ->
- t =
- fun pos op (_, r1) (_, r2) _type_of ->
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> T.boperator -> t -> t -> t =
+ fun ~ctx pos op r1 r2 ->
+ let type_of = Option.get (ctx.f Get_type.expression_id) in
ignore pos;
ignore op;
- r1 @ r2
-end)
+ { type_of; report = r1.report @ r2.report }
+end
module Instruction :
S.Instruction with type t' = Report.t list and type expression = Expression.t' =
diff --git a/lib/checks/nested_strings.mli b/lib/checks/nested_strings.mli
index 1ef2e33..01e373a 100644
--- a/lib/checks/nested_strings.mli
+++ b/lib/checks/nested_strings.mli
@@ -1,3 +1,3 @@
-include Qsp_syntax.S.Analyzer
+include Qsp_syntax.Analyzer.T with type ex = Qsp_syntax.Identifier.t
(** The module [Nested_strings] report errors for each unnecessary raw string
encoded inside a string expression *)
diff --git a/lib/checks/type_of.ml b/lib/checks/type_of.ml
index 42f9a2d..243c8b3 100644
--- a/lib/checks/type_of.ml
+++ b/lib/checks/type_of.ml
@@ -12,12 +12,15 @@ type context = unit
let initialize = Fun.id
let finalize () = []
+let depends = [ Get_type.ex ]
+
+type ex = Qsp_syntax.Identifier.t
module Helper = struct
- type argument_repr = { pos : S.pos; t : Get_type.t }
+ type argument_repr = { pos : S.pos; t : Get_type.Expression.t }
module DynType = struct
- type nonrec t = Get_type.t -> Get_type.t
+ type nonrec t = Get_type.Expression.t -> Get_type.Expression.t
(** Dynamic type is a type unknown during the code.
For example, the equality operator accept either Integer or String, but
@@ -143,35 +146,35 @@ module Helper = struct
msg :: report
end
-type t' = { result : Get_type.t Lazy.t; pos : S.pos }
+type t' = { result : Get_type.Expression.t; pos : S.pos }
-let arg_of_repr : Get_type.t Lazy.t -> S.pos -> Helper.argument_repr =
- fun type_of pos -> { pos; t = Lazy.force type_of }
+let arg_of_repr : Get_type.Expression.t -> S.pos -> Helper.argument_repr =
+ fun type_of pos -> { pos; t = type_of }
-module TypedExpression = struct
+module Expression = struct
type nonrec t' = t' * Report.t list
- type state = { pos : S.pos }
+ type state = { pos : S.pos; type_of : Get_type.Expression.t }
type t = state * Report.t list
- let v : Get_type.t Lazy.t * t -> t' =
- fun (type_of, (t, r)) -> ({ result = type_of; pos = t.pos }, r)
+ let v : t -> t' = fun (t, r) -> ({ result = t.type_of; pos = t.pos }, r)
(** The variable has type string when starting with a '$' *)
- let ident :
- (S.pos, Get_type.t Lazy.t * t) S.variable -> Get_type.t Lazy.t -> t =
- fun var _type_of ->
+ let ident : ctx:Qsp_syntax.S.extract_context -> (S.pos, t) S.variable -> t =
+ fun ~ctx var ->
(* Extract the error from the index *)
let report =
match var.index with
| None -> []
| Some (_, expr) ->
- let _, r = expr in
+ let r = expr in
r
in
- ({ pos = var.pos }, report)
+ let type_of = Option.get (ctx.f Get_type.expression_id) in
+ ({ pos = var.pos; type_of }, report)
- let integer : S.pos -> string -> Get_type.t Lazy.t -> t =
- fun pos value _type_of ->
+ let integer : ctx:Qsp_syntax.S.extract_context -> S.pos -> string -> t =
+ fun ~ctx pos value ->
+ let type_of = Option.get (ctx.f Get_type.expression_id) in
let int_value = int_of_string_opt value in
let report =
@@ -181,42 +184,36 @@ module TypedExpression = struct
| None -> Report.error pos "Invalid integer value" :: []
in
- ({ pos }, report)
+ ({ pos; type_of }, report)
let literal :
- S.pos -> (Get_type.t Lazy.t * t) T.literal list -> Get_type.t Lazy.t -> t
- =
- fun pos values type_of ->
- ignore type_of;
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> t T.literal list -> t =
+ fun ~ctx pos values ->
+ let type_of = Option.get (ctx.f Get_type.expression_id) in
let init = [] in
let report =
List.fold_left values ~init ~f:(fun report -> function
| T.Text _ -> report
- | T.Expression (_, t) ->
+ | T.Expression t ->
let report = List.rev_append (snd t) report in
report)
in
- ({ pos }, report)
+ ({ pos; type_of }, report)
let function_ :
- S.pos ->
- T.function_ ->
- (Get_type.t Lazy.t * t) list ->
- Get_type.t Lazy.t ->
- t =
- fun pos function_ params _type_of ->
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> T.function_ -> t list -> t =
+ fun ~ctx pos function_ params ->
+ let type_of = Option.get (ctx.f Get_type.expression_id) in
(* Accumulate the expressions and get the results, the report is given in
the differents arguments, and we build a list with the type of the
parameters. *)
let types, report =
- List.fold_left params ~init:([], [])
- ~f:(fun (types, report) (type_of, param) ->
- ignore type_of;
+ List.fold_left params ~init:([], []) ~f:(fun (types, report) param ->
let t, r = param in
- let arg = arg_of_repr type_of t.pos in
+ let arg = arg_of_repr t.type_of t.pos in
(arg :: types, r @ report))
in
- let types = List.rev types and default = { pos } in
+ let types = List.rev types and default = { pos; type_of } in
match function_ with
| Arrcomp | Arrpos | Arrsize | Countobj | Desc | Dyneval | Getobj | Instr
@@ -229,7 +226,7 @@ module TypedExpression = struct
let expected = Helper.[ Fixed Bool; Dynamic d; Dynamic d ] in
let report = Helper.compare_args pos expected types report in
(* Extract the type for the expression *)
- ({ pos }, report)
+ ({ pos; type_of }, report)
| Input | Input' ->
(* Input should check the result if the variable is a num and raise a
message in this case.*)
@@ -257,7 +254,7 @@ module TypedExpression = struct
(* All the arguments must have the same type *)
let expected = Helper.[ Variable (Dynamic d) ] in
let report = Helper.compare_args pos expected types report in
- ({ pos }, report)
+ ({ pos; type_of }, report)
| Mid | Mid' ->
let expected = Helper.[ Fixed String; Variable (Fixed Integer) ] in
let report = Helper.compare_args pos expected types report in
@@ -292,29 +289,25 @@ module TypedExpression = struct
(** Unary operator like [-123] or [+'Text']*)
let uoperator :
- S.pos -> T.uoperator -> Get_type.t Lazy.t * t -> Get_type.t Lazy.t -> t =
- fun pos operator t1 type_of ->
- ignore type_of;
- let type_of, (t, report) = t1 in
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> T.uoperator -> t -> t =
+ fun ~ctx pos operator t1 ->
+ let t, report = t1 in
match operator with
| Add -> (t, report)
| Neg | No ->
- let types = [ arg_of_repr type_of t.pos ] in
+ let types = [ arg_of_repr t.type_of t.pos ] in
let expected = Helper.[ Fixed Integer ] in
let report = Helper.compare_args pos expected types report in
- ({ pos }, report)
+ let type_of = Option.get (ctx.f Get_type.expression_id) in
+ ({ pos; type_of }, report)
let boperator :
- S.pos ->
- T.boperator ->
- Get_type.t Lazy.t * t ->
- Get_type.t Lazy.t * t ->
- Get_type.t Lazy.t ->
- t =
- fun pos operator (type_1, t1) (type_2, t2) type_of ->
- ignore type_of;
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> T.boperator -> t -> t -> t =
+ fun ~ctx pos operator t1 t2 ->
let t1, report1 = t1 in
let t2, report2 = t2 in
+ let type_1 = t1.type_of and type_2 = t2.type_of in
+ let type_of = Option.get (ctx.f Get_type.expression_id) in
let report = report1 @ report2 in
@@ -327,7 +320,7 @@ module TypedExpression = struct
When concatenating, it’s allowed to add an integer and a number.
*)
- ({ pos }, report)
+ ({ pos; type_of }, report)
| T.Eq | T.Neq | Lt | Gte | Lte | Gt ->
(* If the expression is '' or 0, we accept the comparaison as if
instead of raising a warning *)
@@ -345,26 +338,24 @@ module TypedExpression = struct
report
| report -> report
in
- ({ pos }, report)
+ ({ pos; type_of }, report)
| T.Mod | T.Minus | T.Product | T.Div ->
(* Operation over number *)
let expected = Helper.[ Fixed Integer; Fixed Integer ] in
let report = Helper.compare_args pos expected types report in
- ({ pos }, report)
+ ({ pos; type_of }, report)
| T.And | T.Or ->
(* Operation over booleans *)
let expected = Helper.[ Fixed Bool; Fixed Bool ] in
let report = Helper.compare_args pos expected types report in
- ({ pos }, report)
+ ({ pos; type_of }, report)
end
-module Expression = Compose.TypeBuilder.Make (TypedExpression)
-
module Instruction = struct
type t = Report.t list
type t' = Report.t list
- let v : t -> t' = fun local_report -> local_report
+ let v : t -> t' = Fun.id
type expression = Expression.t'
@@ -444,17 +435,21 @@ module Instruction = struct
let report = List.rev_append report' report in
- match (op, Get_type.get_type (Lazy.force right_expression.result)) with
+ match (op, Get_type.Expression.get_type right_expression.result) with
| T.Eq', Get_type.Integer ->
(* Assigning an intger is allowed in a string variable, but raise a
warning. *)
- let var_type = Lazy.from_val (Get_type.ident variable) in
+ let var_type =
+ Get_type.Expression.ident ~ctx:{ f = (fun _ -> None) } variable
+ in
let op1 = arg_of_repr var_type variable.pos in
let expected = Helper.[ Fixed Integer ] in
Helper.compare_args ~strict:true ~level:Report.Warn pos expected [ op1 ]
report
| _, _ -> (
- let var_type = Lazy.from_val (Get_type.ident variable) in
+ let var_type =
+ Get_type.Expression.ident ~ctx:{ f = (fun _ -> None) } variable
+ in
let op1 = arg_of_repr var_type variable.pos in
let op2 = arg_of_repr right_expression.result right_expression.pos in
diff --git a/lib/checks/type_of.mli b/lib/checks/type_of.mli
index de0f8f9..f2be559 100644
--- a/lib/checks/type_of.mli
+++ b/lib/checks/type_of.mli
@@ -1,7 +1,7 @@
-include Qsp_syntax.S.Analyzer
+include Qsp_syntax.Analyzer.T with type ex = Qsp_syntax.Identifier.t
(** The module [type_of] populate the report with differents inconsistency
- errors in the types.
+ errors in the types.
- - Assigning a [string] value in an [integer] variable
- - Comparing a [string] with an [integer]
- - Giving the wrong type in the argument for a function and so one. *)
+ - Assigning a [string] value in an [integer] variable
+ - Comparing a [string] with an [integer]
+ - Giving the wrong type in the argument for a function and so one. *)
diff --git a/lib/checks/write_only.ml b/lib/checks/write_only.ml
index e2c3d7e..2d78b59 100644
--- a/lib/checks/write_only.ml
+++ b/lib/checks/write_only.ml
@@ -15,6 +15,9 @@ let description = "Check variables never read"
let active = ref false
let is_global = true
+let depends = []
+
+type ex = Qsp_syntax.Identifier.t
module StringMap = Hashtbl.Make (String)
module Set = Set.Make (String)
@@ -76,13 +79,16 @@ module Expression = struct
let default _ map = ignore map
end)
- let ident : (S.pos, t) S.variable -> t =
- fun variable filename map ->
+ let ident : ctx:Qsp_syntax.S.extract_context -> (S.pos, t) S.variable -> t =
+ fun ~ctx variable filename map ->
+ ignore ctx;
(* Update the map and set the read flag *)
set_readed variable.pos variable.name filename map
- let literal : S.pos -> t T.literal list -> t =
- fun pos l filename map ->
+ let literal :
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> t T.literal list -> t =
+ fun ~ctx pos l filename map ->
+ ignore ctx;
List.iter l ~f:(function
| T.Text t ->
set_readed pos ~update_only:true (String.uppercase_ascii t) filename
@@ -91,13 +97,22 @@ module Expression = struct
(* When the string contains an expression evaluate it *)
exprs filename map)
- let function_ : S.pos -> T.function_ -> t list -> t =
- fun _ _ exprs filename map -> List.iter ~f:(fun v -> v filename map) exprs
+ let function_ :
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> T.function_ -> t list -> t =
+ fun ~ctx _ _ exprs filename map ->
+ ignore ctx;
+ List.iter ~f:(fun v -> v filename map) exprs
- let uoperator : S.pos -> T.uoperator -> t -> t = fun _ _ t map -> t map
+ let uoperator :
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> T.uoperator -> t -> t =
+ fun ~ctx _ _ t map ->
+ ignore ctx;
+ t map
- let boperator : S.pos -> T.boperator -> t -> t -> t =
- fun _ _ t1 t2 filename map ->
+ let boperator :
+ ctx:Qsp_syntax.S.extract_context -> S.pos -> T.boperator -> t -> t -> t =
+ fun ~ctx _ _ t1 t2 filename map ->
+ ignore ctx;
t1 filename map;
t2 filename map
end