diff options
Diffstat (limited to 'lib/syntax')
| -rw-r--r-- | lib/syntax/check.ml | 429 | ||||
| -rw-r--r-- | lib/syntax/check.mli | 53 | ||||
| -rw-r--r-- | lib/syntax/compose.ml | 125 | ||||
| -rw-r--r-- | lib/syntax/dead_end.ml | 171 | ||||
| -rw-r--r-- | lib/syntax/dead_end.mli | 6 | ||||
| -rw-r--r-- | lib/syntax/default.ml | 41 | ||||
| -rw-r--r-- | lib/syntax/dup_test.ml | 188 | ||||
| -rw-r--r-- | lib/syntax/dup_test.mli | 1 | ||||
| -rw-r--r-- | lib/syntax/get_type.ml | 121 | ||||
| -rw-r--r-- | lib/syntax/locations.ml | 159 | ||||
| -rw-r--r-- | lib/syntax/nested_strings.ml | 156 | ||||
| -rw-r--r-- | lib/syntax/nested_strings.mli | 1 | ||||
| -rw-r--r-- | lib/syntax/type_of.ml | 488 | ||||
| -rw-r--r-- | lib/syntax/type_of.mli | 7 | ||||
| -rw-r--r-- | lib/syntax/write_only.ml | 217 | 
15 files changed, 0 insertions, 2163 deletions
diff --git a/lib/syntax/check.ml b/lib/syntax/check.ml deleted file mode 100644 index b642945..0000000 --- a/lib/syntax/check.ml +++ /dev/null @@ -1,429 +0,0 @@ -module Id = Type.Id - -(** The the Id module, wrap a value in an existencial type with a witness -    associate with. *) -type result = R : { value : 'a; witness : 'a Id.t } -> result - -let get : type a. a Id.t -> result -> a option = - fun typeid (R { value; witness }) -> -  match Id.provably_equal typeid witness with -  | Some Type.Equal -> Some value -  | None -> None - -type t = -  | E : { -      module_ : -        (module S.Analyzer -           with type Expression.t = 'a -            and type Expression.t' = 'b -            and type Instruction.t = 'c -            and type Instruction.t' = 'd -            and type Location.t = 'e -            and type context = 'f); -      expr_witness : 'a Id.t; -      expr' : 'b Id.t; -      instr_witness : 'c Id.t; -      instr' : 'd Id.t; -      location_witness : 'e Id.t; -      context : 'f Id.t; -    } -      -> t - -let build : -    (module S.Analyzer -       with type Expression.t = _ -        and type Expression.t' = _ -        and type Instruction.t = _ -        and type Instruction.t' = _ -        and type Location.t = 'a -        and type context = _) -> -    'a Id.t * t = - fun module_ -> -  let expr_witness = Id.make () -  and expr' = Id.make () -  and instr_witness = Id.make () -  and instr' = Id.make () -  and location_witness = Id.make () -  and context = Id.make () in -  let t = -    E -      { -        module_; -        expr_witness; -        expr'; -        instr_witness; -        instr'; -        location_witness; -        context; -      } -  in -  (location_witness, t) - -let get_module : t -> (module S.Analyzer) = - fun (E { module_; _ }) -> (module_ :> (module S.Analyzer)) - -module type App = sig -  val t : t array -end - -open StdLabels - -module Helper = struct -  type 'a expr_list = { witness : 'a Id.t; values : 'a list } - -  let expr_i : result array list -> 'a Id.t -> int -> 'a expr_list = -   fun args witness i -> -    let result = -      List.fold_left args ~init:{ values = []; witness } -        ~f:(fun (type a) ({ values; witness } : a expr_list) t : a expr_list -> -          match get witness (Array.get t i) with -          | None -> failwith "Does not match" -          | Some value_1 -> { values = value_1 :: values; witness }) -    in -    { result with values = result.values } -end - -module Make (A : App) = struct -  let identifier = "main_checker" -  let description = "Internal module" -  let is_global = false -  let active = ref false - -  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 each test, and keep the result in the context. *) -  let initialize : unit -> context = -   fun () -> -    Array.map A.t ~f:(fun (E { module_ = (module S); context; _ }) -> -        let value = S.initialize () in -        R { value; witness = context }) - -  let finalize : result Array.t -> (string * Report.t) list = -   fun context_array -> -    let _, report = -      Array.fold_left A.t ~init:(0, []) -        ~f:(fun (i, acc) (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 -          (i + 1, List.rev_append reports acc)) -    in -    report - -  (* Global variable for the whole module *) -  let len = Array.length A.t - -  module Expression : S.Expression with type t' = result array = struct -    type t = result array -    type t' = result array - -    let literal : S.pos -> t T.literal list -> t = -     fun pos values -> -      Array.mapi A.t ~f:(fun i (E { module_ = (module S); expr_witness; _ }) -> -          (* Map every values to the Checker *) -          let values' = -            List.map values -              ~f: -                (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 (E { module_ = (module S); expr_witness; _ }) -> -          let value = S.Expression.integer pos value in -          R { value; witness = expr_witness }) - -    (** Unary operator like [-123] or [+'Text']*) -    let uoperator : S.pos -> T.uoperator -> t -> t = -     fun pos op values -> -      (* Evaluate the nested expression *) -      let results = values in - -      (* Now evaluate the remaining expression. - -         Traverse both the module the apply, and the matching expression already -         evaluated. - -         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 (E { module_ = (module S); expr_witness; _ }) value -> -            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 }) -      in -      results - -    (** Basically the same as uoperator, but operate over two operands instead -        of a single one. *) -    let boperator : S.pos -> 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") - -    (** Call a function. The functions list is hardcoded in lib/lexer.mll *) -    let function_ : S.pos -> 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 })) - -    (** Convert each internal represention for the expression into its external -        representation *) -    let v : t -> t' = -     fun t -> -      let result = -        Array.map2 A.t t -          ~f:(fun (E { module_ = (module S); expr_witness; expr'; _ }) result -> -            match get expr_witness result with -            | None -> failwith "Does not match" -            | Some value -> -                let value = S.Expression.v value in -                R { witness = expr'; value }) -      in -      result -  end - -  module Instruction : -    S.Instruction -      with type expression = Expression.t' -       and type t' = result array = struct -    type expression = Expression.t' -    type t = result array -    type t' = result array - -    let location : S.pos -> string -> t = -     fun pos label -> -      Array.map A.t ~f:(fun (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 (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 -        ~f:(fun (E { module_ = (module S); instr_witness; expr'; _ }) result -> -          match get expr' result with -          | None -> failwith "Does not match" -          | Some value -> -              (* The evaluate the instruction *) -              let value = S.Instruction.expression value in -              R { value; witness = instr_witness }) - -    let call : S.pos -> T.keywords -> expression list -> t = -     fun pos keyword args -> -      (* The arguments are given like an array of array. Each expression is -         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 -          in - -          let values = List.rev (Helper.expr_i args expr' i).values in - -          let value = S.Instruction.call pos keyword values in -          R { witness = instr_witness; value }) - -    let act : S.pos -> label:expression -> t list -> t = -     fun pos ~label instructions -> -      Array.init len ~f:(fun i -> -          let (E { module_ = (module S); instr_witness; expr'; _ }) = -            Array.get A.t i -          in -          let values = -            List.rev (Helper.expr_i instructions instr_witness i).values -          in - -          match get expr' (Array.get label i) with -          | None -> failwith "Does not match" -          | Some label_i -> -              let value = S.Instruction.act pos ~label:label_i values in -              R { witness = instr_witness; value }) - -    (* I think it’s one of the longest module I’ve ever written in OCaml… *) - -    let assign : -        S.pos -> -        (S.pos, expression) S.variable -> -        T.assignation_operator -> -        expression -> -        t = -     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 -          in - -          let index_i = -            Option.map -              (fun expression -> -                Option.get (get expr' (Array.get expression i))) -              index -          in -          let variable = S.{ pos = var_pos; name; index = index_i } in - -          match get expr' (Array.get expression i) with -          | None -> failwith "Does not match" -          | Some value -> -              let value = A.Instruction.assign pos variable op value in - -              R { value; witness = instr_witness }) - -    let rebuild_clause : -        type a b. -        int -> -        a Id.t -> -        b Id.t -> -        S.pos * result array * result array list -> -        (b, a) S.clause = -     fun i instr_witness expr' clause -> -      let pos_clause, expr_clause, ts = clause in -      match get expr' (Array.get expr_clause i) with -      | None -> failwith "Does not match" -      | Some value -> -          let ts = Helper.expr_i ts instr_witness i in -          let ts = List.rev ts.values in -          let clause = (pos_clause, value, ts) in -          clause - -    let if_ : -        S.pos -> -        (expression, t) S.clause -> -        elifs:(expression, t) S.clause list -> -        else_:(S.pos * t list) option -> -        t = -     fun pos clause ~elifs ~else_ -> -      (* First, apply the report for all the instructions *) -      let else_ = -        match else_ with -        | None -> None -        | Some (pos, instructions) -> Some (pos, instructions) -      in -      Array.init len ~f:(fun i -> -          let (E { module_ = (module A); instr_witness; expr'; _ }) = -            Array.get A.t i -          in - -          let clause = rebuild_clause i instr_witness expr' clause -          and elifs = List.map elifs ~f:(rebuild_clause i instr_witness expr') -          and else_ = -            match else_ with -            | None -> None -            | Some (pos, instructions) -> -                let elses = Helper.expr_i instructions instr_witness i in -                Some (pos, List.rev elses.values) -          in - -          let value = A.Instruction.if_ pos clause ~elifs ~else_ in -          R { value; witness = instr_witness }) - -    (** This code is almost a copy/paste from Expression.v but I did not found -        a way to factorize it. *) -    let v : t -> t' = -     fun t -> -      let result = -        Array.map2 A.t t -          ~f:(fun -              (E { module_ = (module S); instr_witness; instr'; _ }) result -> -            match get instr_witness result with -            | None -> failwith "Does not match" -            | Some value -> -                let value = S.Instruction.v value in -                R { witness = instr'; value }) -      in -      result -  end - -  module Location : -    S.Location -      with type t = result array -       and type instruction = Instruction.t' -       and type context := context = struct -    type instruction = Instruction.t' -    type t = result array - -    let location : context -> S.pos -> instruction list -> t = -     fun local_context pos args -> -      ignore pos; - -      let result = -        Array.init len ~f:(fun i -> -            let (E -                  { module_ = (module A); instr'; location_witness; context; _ }) -                = -              Array.get A.t i -            in - -            let local_context = -              Option.get (get context (Array.get local_context i)) -            in - -            let instructions = List.rev (Helper.expr_i args instr' i).values in -            let value = A.Location.location local_context pos instructions in -            R { value; witness = location_witness }) -      in -      result - -    let v : t -> Report.t list = -     fun args -> -      let report = ref [] in -      let () = -        Array.iteri args ~f:(fun i result -> -            let (E { module_ = (module A); location_witness; _ }) = -              Array.get A.t i -            in -            match get location_witness result with -            | None -> failwith "Does not match" -            | Some value -> -                let re = A.Location.v value in -                report := List.rev_append re !report) -      in -      !report -  end -end diff --git a/lib/syntax/check.mli b/lib/syntax/check.mli deleted file mode 100644 index 7db719d..0000000 --- a/lib/syntax/check.mli +++ /dev/null @@ -1,53 +0,0 @@ -(** This module is a meta-checker. It will take many checkers and aggregate -    their result together before providing an unified result.  - -    The modules required to be declared before being used, using the [build] -    method, and provided as an array : - -    {[  -    let _, e1 = build (module …) -    let _, e2 = build (module …) -     -    module Check = Make (struct -      let t = [| e1; e2 |] -    end) -    ]} -*) - -module Id : sig -  type 'a t -  (** The type created on-the-fly. *) -end - -type t -(** Type of check to apply *) - -val build : -  (module S.Analyzer -     with type Expression.t = _ -      and type Expression.t' = _ -      and type Instruction.t = _ -      and type Instruction.t' = _ -      and type Location.t = 'a -      and type context = _) -> -  'a Id.t * t -(** Build a new check from a module following S.Analyzer signature.  -ypeid  -    Return the result type which hold the final result value, and checker -    itself. *) - -val get_module : t -> (module S.Analyzer) - -type result - -val get : 'a Id.t -> result -> 'a option -(** The method [get] can be used to get the internal value for one of the -    checker used. - *) - -module Make (A : sig -  val t : t array -end) : sig -  include S.Analyzer with type Location.t = result array -end -[@@warning "-67"] diff --git a/lib/syntax/compose.ml b/lib/syntax/compose.ml deleted file mode 100644 index 8c92ed0..0000000 --- a/lib/syntax/compose.ml +++ /dev/null @@ -1,125 +0,0 @@ -(** Build a module with the result from another one module *) - -open StdLabels - -(** 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 diff --git a/lib/syntax/dead_end.ml b/lib/syntax/dead_end.ml deleted file mode 100644 index c0dbc58..0000000 --- a/lib/syntax/dead_end.ml +++ /dev/null @@ -1,171 +0,0 @@ -open StdLabels - -let identifier = "dead_end" -let description = "Check for dead end in the code" -let is_global = false -let active = ref false - -type context = unit - -let initialize = Fun.id -let finalize () = [] - -module Expression = struct -  type t = unit - -  include Default.Expression (struct -    type nonrec t = t - -    let default = () -  end) - -  let v : t -> t' = fun () -> () -end - -module Instruction = struct -  type cause = Missing_else | Unchecked_path - -  type state = { -    block_pos : S.pos; -    has_gt : bool; -    is_gt : bool; -    pos : (cause * S.pos) option; -  } - -  type t = state -  type t' = state - -  (** For each instruction, return thoses two informations : - -      - the intruction contains at [gt]  -      - the last instruction is a [gt] - -    *) -  let v : t -> t' = fun t -> t - -  let default = -    { -      block_pos = (Lexing.dummy_pos, Lexing.dummy_pos); -      has_gt = false; -      is_gt = false; -      pos = None; -    } - -  (** Call for an instruction like [GT] or [*CLR] *) -  let call : S.pos -> T.keywords -> Expression.t' list -> t = -   fun pos f _ -> -    ignore pos; -    match f with -    | T.Goto | T.XGoto -> -        { block_pos = pos; has_gt = true; is_gt = true; pos = None } -    | T.Gosub -> { block_pos = pos; has_gt = false; is_gt = true; pos = None } -    | _ -> default - -  (** Label for a loop *) -  let location : S.pos -> string -> t = fun _ _ -> default - -  (** Comment *) -  let comment : S.pos -> t = fun _ -> default - -  (** 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 *) -  let check_block : S.pos -> t list -> t = -   fun pos instructions -> -    let last_element = -      List.fold_left instructions ~init:default ~f:(fun t instruction -> -          let result = instruction in -          let has_gt = result.has_gt || t.has_gt in -          let is_gt = result.is_gt || t.is_gt in -          { result with block_pos = pos; is_gt; has_gt }) -    in -    last_element - -  let if_ : -      S.pos -> -      (Expression.t', t) S.clause -> -      elifs:(Expression.t', t) S.clause list -> -      else_:(S.pos * t list) option -> -      t = -   fun pos clause ~elifs ~else_ -> -    (* For each block, evaluate the instructions *) -    let res, has_gt, is_gt = -      List.fold_left ~init:([], false, false) (clause :: elifs) -        ~f:(fun (acc, has_gt, is_gt) clause -> -          let pos, _, instructions = clause in -          let clause_t = check_block pos instructions in -          let has_gt = has_gt || clause_t.has_gt -          and is_gt = is_gt || clause_t.is_gt in - -          ((clause_t, pos) :: acc, has_gt, is_gt)) -    in - -    let else_pos, else_block = -      match else_ with -      | Some (pos, instructions) -> -          let block = check_block pos instructions in -          (pos, block) -      | None -> (pos, default) -    in -    let has_gt = has_gt || else_block.has_gt -    and is_gt = is_gt || else_block.is_gt in - -    let blocks = (else_block, else_pos) :: res in - -    (* Check if one of the clauses already holds a dead end*) -    match List.find_opt res ~f:(fun (res, _) -> res.pos != None) with -    | Some (v, _) -> v -    | None -> ( -        match (is_gt, has_gt) with -        | _, true -> ( -            (* There is gt intruction in one of the branch, we need to checks -               the others *) -            match List.find_opt blocks ~f:(fun (f, _) -> not f.is_gt) with -            | None -> -                (* Every branch in the if is covered. It’s ok. *) -                { default with block_pos = pos; is_gt; has_gt } -            | Some (_, pos) -> -                (* TODO check if [pos] is the whole block *) -                let cause = -                  match else_ with None -> Missing_else | _ -> Unchecked_path -                in -                { default with block_pos = pos; pos = Some (cause, pos) }) -        | _, _ -> { default with block_pos = pos; has_gt; is_gt }) - -  let act : S.pos -> label:Expression.t' -> t list -> t = -   fun pos ~label expressions -> -    ignore label; -    check_block pos expressions - -  let assign : -      S.pos -> -      (S.pos, Expression.t') S.variable -> -      T.assignation_operator -> -      Expression.t' -> -      t = -   fun _ _ _ _ -> default -end - -module Location = struct -  type t = Report.t list - -  let v = Fun.id - -  let location : unit -> S.pos -> Instruction.t' list -> t = -   fun () _pos instructions -> -    List.fold_left instructions ~init:[] ~f:(fun report t -> -        match (t.Instruction.is_gt, t.Instruction.pos) with -        | false, Some (cause, value) -> -            ignore cause; -            if t.Instruction.block_pos != value then -              match cause with -              | Missing_else -> -                  Report.debug value "Possible dead end (no else fallback)" -                  :: report -              | Unchecked_path -> -                  Report.warn value "Possible dead end (unmatched path)" -                  :: report -            else report -        | _ -> report) -end diff --git a/lib/syntax/dead_end.mli b/lib/syntax/dead_end.mli deleted file mode 100644 index 451fe58..0000000 --- a/lib/syntax/dead_end.mli +++ /dev/null @@ -1,6 +0,0 @@ -(** Checker looking for the dead ends in the source.  - -    A dead end is a state where the user does not have any action. - *) - -include S.Analyzer diff --git a/lib/syntax/default.ml b/lib/syntax/default.ml deleted file mode 100644 index d345401..0000000 --- a/lib/syntax/default.ml +++ /dev/null @@ -1,41 +0,0 @@ -(** Default implementation which does nothing.  - -This module is expected to be used when you only need to implement an analyze -over a limited part of the whole syntax. *) - -module type T = sig -  type t - -  val default : t -end - -module Expression (T' : T) = struct -  (**  -      Describe a variable, using the name in capitalized text, and an optionnal -      index. - -      If missing, the index should be considered as [0]. -   *) - -  type t' = T'.t - -  let ident : (S.pos, T'.t) S.variable -> T'.t = fun _ -> 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 - -  (** 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 - -  (** Unary operator like [-123] or [+'Text']*) -  let uoperator : S.pos -> T.uoperator -> T'.t -> T'.t = fun _ _ _ -> 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 -end diff --git a/lib/syntax/dup_test.ml b/lib/syntax/dup_test.ml deleted file mode 100644 index 20faa56..0000000 --- a/lib/syntax/dup_test.ml +++ /dev/null @@ -1,188 +0,0 @@ -(** This module check for duplicated tests in the source.contents - - -    This in intended to identify the copy/paste errors, where one location -    check for the same arguments twice or more. - *) - -open StdLabels - -let identifier = "duplicate_test" -let description = "Check for duplicate tests" -let is_global = false -let active = ref true - -type context = unit - -let initialize = Fun.id -let finalize () = [] - -module Expression = Tree.Expression - -(** Build a Hashtbl over the expression, ignoring the location in the -    expression *) -module Table = Hashtbl.Make (struct -  type t = Expression.t' - -  let equal : t -> t -> bool = Tree.Expression.eq (fun _ _ -> true) -  let hash : t -> int = Tree.Expression.hash (fun _ -> 0) -end) - -module Instruction = struct -  type state = { -    predicates : (Expression.t' * S.pos) list; -    duplicates : (Expression.t' * S.pos list) list; -  } -  (** Keep the list of all the predicates and their position in a block, and -      the list of all the identified duplicated values. *) - -  type t = state -  type t' = state - -  let v : t -> t' = fun t -> t -  let default = { predicates = []; duplicates = [] } - -  (** Label for a loop *) -  let location : S.pos -> string -> t = fun _ _ -> default - -  (** Comment *) -  let comment : S.pos -> t = fun _ -> default - -  (** Raw expression *) -  let expression : Expression.t' -> t = fun _ -> default - -  let check_duplicates : -      (Expression.t' * S.pos) list -> (Expression.t' * S.pos list) list = -   fun predicates -> -    let table = Table.create 5 in -    let () = List.to_seq predicates |> Table.add_seq table in - -    Table.to_seq_keys table -    |> Seq.group (Tree.Expression.eq (fun _ _ -> true)) -    |> Seq.filter_map (fun keys -> -           (* Only take the first element for each group, we don’t need to -              repeat the key *) -           match Seq.uncons keys with -           | None -> None -           | Some (hd, _) -> ( -               match Table.find_all table hd with -               | [] | _ :: [] -> None -               | other -> Some (hd, other))) -    |> List.of_seq - -  (** Evaluate a clause.  -      This function does two things :  -          - report all errors from the bottom to top  -          - add the clause in the actual level *) -  let predicate_of_clause : ?pos:S.pos -> t -> (Expression.t', t) S.clause -> t -      = -   fun ?pos t (pos2, predicate, blocks) -> -    let pos = Option.value ~default:pos2 pos in - -    (* Remove the clauses using the function rnd because they repeating the -       same clause can generate a different result *) -    let should_discard = -      Tree.Expression.exists predicate ~f:(function -        | Tree.Ast.Function (_, T.Rand, _) | Tree.Ast.Function (_, T.Rnd, _) -> -            true -        | _ -> false) -    in - -    { -      predicates = -        (match should_discard with -        | false -> (predicate, pos) :: t.predicates -        | true -> t.predicates); -      duplicates = -        List.fold_left blocks ~init:t.duplicates ~f:(fun acc t -> -            List.rev_append t.duplicates acc); -    } - -  let if_ : -      S.pos -> -      (Expression.t', t) S.clause -> -      elifs:(Expression.t', t) S.clause list -> -      else_:(S.pos * t list) option -> -      t = -   fun pos clause ~elifs ~else_ -> -    ignore else_; -    (* Collect all the if clauses from this block, wait for the parent block to -       check each case for duplicates. *) -    let init = predicate_of_clause ~pos default clause in -    let state = List.fold_left elifs ~init ~f:predicate_of_clause in -    { -      state with -      duplicates = check_duplicates state.predicates @ state.duplicates; -    } - -  let act : S.pos -> label:Expression.t' -> t list -> t = -   fun _pos ~label expressions -> -    ignore label; -    (* Collect all the elements reported from bottom to up. *) -    List.fold_left ~init:default expressions ~f:(fun state ex -> -        { -          predicates = []; -          duplicates = List.rev_append ex.duplicates state.duplicates; -        }) - -  let assign : -      S.pos -> -      (S.pos, Expression.t') S.variable -> -      T.assignation_operator -> -      Expression.t' -> -      t = -   fun _ _ _ _ -> default - -  let call : S.pos -> T.keywords -> Expression.t' list -> t = -   fun _ _ _ -> default -end - -module Location = struct -  type t = (Expression.t' * S.pos list) list - -  type context = unit -  (** No context *) - -  (** Check if the given expression is involving the variable ARGS or $ARGS *) -  let is_args : Expression.t' -> bool = function -    | Tree.Ast.Ident { name; _ } -> -        String.equal name "ARGS" || String.equal name "$ARGS" -    | _ -> false - -  let location : context -> S.pos -> Instruction.t' list -> t = -   fun () _ block -> -    (* Filter the tests from the top level and only keep them testing ARGS *) -    let duplicates = -      List.map block ~f:(fun t -> -          List.filter_map t.Instruction.predicates ~f:(fun v -> -              match (Tree.Expression.exists ~f:is_args) (fst v) with -              | true -> Some v -              | false -> None)) -      |> List.concat |> Instruction.check_duplicates -    in -    List.fold_left ~init:duplicates block ~f:(fun state ex -> -        List.rev_append ex.Instruction.duplicates state) - -  (** Create the report message *) -  let v' : Expression.t' * S.pos list -> Report.t option = -   fun (expr, pos) -> -    ignore expr; -    match (List.sort ~cmp:Report.compare_pos) pos with -    | [] -> None -    | _ :: [] -> None -    | hd :: tl -> -        let message = -          Format.asprintf "This case is duplicated line(s) %a" -            (Format.pp_print_list -               ~pp_sep:(fun f () -> Format.pp_print_char f ',') -               Report.pp_line) -            tl -        in - -        (* Report all the messages as error. They do not break the game, but -           there is no question if it should *) -        Some (Report.error hd message) - -  let v : t -> Report.t list = -   fun t -> List.filter_map t ~f:v' |> List.sort_uniq ~cmp:Report.compare -end diff --git a/lib/syntax/dup_test.mli b/lib/syntax/dup_test.mli deleted file mode 100644 index 38e3a1b..0000000 --- a/lib/syntax/dup_test.mli +++ /dev/null @@ -1 +0,0 @@ -include S.Analyzer diff --git a/lib/syntax/get_type.ml b/lib/syntax/get_type.ml deleted file mode 100644 index b22f53c..0000000 --- a/lib/syntax/get_type.ml +++ /dev/null @@ -1,121 +0,0 @@ -open StdLabels - -type type_of = -  | Integer  (** A numeric value *) -  | Bool  (** A boolean, not a real type  *) -  | String  (** String value *) -  | NumericString -      [@printer fun fmt _ -> Format.pp_print_string fmt "Integer as String"] -      (** 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 -             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 -> Raw Integer -  | Loc -> Variable Bool -  | Max | Max' | Min | Min' -> ( -      try List.hd params with Failure _ -> Raw Bool) -  | Mid | Mid' -> Variable String -  | Msecscount -> Raw Integer -  | Rand -> Raw Integer -  | Replace -> Variable String -  | Replace' -> Variable String -  | Rgb -> Raw Integer -  | Rnd -> Raw Integer -  | Selact -> Variable String -  | Str | Str' -> Raw String -  | Strcomp -> Raw Bool -  | Strfind -> Variable String -  | Strfind' -> Variable String -  | Strpos -> Raw Integer -  | Trim -> Variable String -  | Trim' -> Variable String -  | Val -> Raw Integer diff --git a/lib/syntax/locations.ml b/lib/syntax/locations.ml deleted file mode 100644 index 17f33bd..0000000 --- a/lib/syntax/locations.ml +++ /dev/null @@ -1,159 +0,0 @@ -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 -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 diff --git a/lib/syntax/nested_strings.ml b/lib/syntax/nested_strings.ml deleted file mode 100644 index dee7af0..0000000 --- a/lib/syntax/nested_strings.ml +++ /dev/null @@ -1,156 +0,0 @@ -open StdLabels - -let identifier = "escaped_string" -let description = "Check for unnecessary use of expression encoded in string" -let is_global = false -let active = ref true - -type context = unit - -let initialize = Fun.id -let finalize () = [] - -module TypeBuilder = Compose.Expression (Get_type) - -module Expression = TypeBuilder.Make (struct -  type t = Report.t list -  type t' = Report.t list - -  let v : Get_type.t Lazy.t * t -> t' = snd - -  (** 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 -> -    match content with -    | [ T.Expression (t', _); T.Text "" ] -> ( -        match Get_type.get_type (Lazy.force t') with -        | Get_type.Integer -> [] -        | _ -> -            let msg = Report.debug pos "This expression can be simplified" in -            [ msg ]) -    | _ -> [] - -  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 integer : S.pos -> string -> Get_type.t Lazy.t -> t = -   fun pos t _type_of -> -    ignore pos; -    ignore t; -    [] - -  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 -> -    ignore pos; -    ignore f; -    let exprs = -      List.fold_left ~init:[] expressions ~f:(fun acc el -> -          List.rev_append (snd el) acc) -    in -    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 -> -    ignore op; -    ignore pos; -    snd r - -  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 -> -    ignore pos; -    ignore op; -    r1 @ r2 -end) - -module Instruction : -  S.Instruction with type t' = Report.t list and type expression = Expression.t' = -struct -  type t = Report.t list -  (** Internal type used in the evaluation *) - -  type t' = t - -  let v : t -> t' = Fun.id - -  type expression = Expression.t' - -  let call : S.pos -> T.keywords -> expression list -> t = -   fun pos k exprs -> -    ignore pos; -    ignore k; -    List.concat exprs - -  let location : S.pos -> string -> t = fun _ _ -> [] -  let comment : S.pos -> t = fun _ -> [] -  let expression : expression -> t = Fun.id - -  let act : S.pos -> label:expression -> t list -> t = -   fun pos ~label instructions -> -    ignore pos; -    List.concat (label :: instructions) - -  let fold_clause : (expression, t) S.clause -> t = -   fun (_pos1, expression, ts) -> List.concat (expression :: ts) - -  let if_ : -      S.pos -> -      (expression, t) S.clause -> -      elifs:(expression, t) S.clause list -> -      else_:(S.pos * t list) option -> -      t = -   fun pos clause ~elifs ~else_ -> -    ignore pos; - -    let init = -      match else_ with -      | None -> fold_clause clause -      | Some (_, ts) -> List.rev_append (fold_clause clause) (List.concat ts) -    in - -    List.fold_left elifs ~init ~f:(fun t clause -> -        List.rev_append (fold_clause clause) t) - -  let assign : -      S.pos -> -      (S.pos, expression) S.variable -> -      T.assignation_operator -> -      expression -> -      t = -   fun pos variable op expression -> -    ignore pos; -    ignore op; -    match variable.index with -    | None -> expression -    | Some v -> List.rev_append v expression -end - -module Location = struct -  type t = Report.t list -  type instruction = Instruction.t' - -  let v = Fun.id - -  let location : unit -> S.pos -> instruction list -> t = -   fun () pos intructions -> -    ignore pos; -    List.concat intructions -end diff --git a/lib/syntax/nested_strings.mli b/lib/syntax/nested_strings.mli deleted file mode 100644 index 38e3a1b..0000000 --- a/lib/syntax/nested_strings.mli +++ /dev/null @@ -1 +0,0 @@ -include S.Analyzer diff --git a/lib/syntax/type_of.ml b/lib/syntax/type_of.ml deleted file mode 100644 index 97b7f91..0000000 --- a/lib/syntax/type_of.ml +++ /dev/null @@ -1,488 +0,0 @@ -open StdLabels - -let identifier = "type_check" -let description = "Ensure all the expression are correctly typed" -let is_global = false -let active = ref true - -type context = unit - -let initialize = Fun.id -let finalize () = [] - -module Helper = struct -  type argument_repr = { pos : S.pos; t : Get_type.t } - -  module DynType = struct -    type nonrec t = Get_type.t -> Get_type.t -    (** Dynamic type is a type unknown during the code. - -      For example, the equality operator accept either Integer or String, but -      we expect that both sides of the equality uses the same type.*) - -    (** Build a new dynamic type *) -    let t : unit -> t = -     fun () -> -      let stored = ref None in -      fun t -> -        match !stored with -        | None -> -            stored := Some t; -            t -        | Some t -> t -  end - -  (** Declare an argument for a function.  - - - Either we already know the type and we just have to compare. - - Either the type shall constrained by another one  - - Or we have a variable number of arguments. *) -  type argument = -    | Fixed of Get_type.type_of -    | Dynamic of DynType.t -    | Variable of argument - -  let compare : -      ?level:Report.level -> -      strict:bool -> -      Get_type.type_of -> -      argument_repr -> -      Report.t list -> -      Report.t list = -   fun ?(level = Report.Warn) ~strict expected actual report -> -    let equal = -      match (expected, actual.t) with -      (* Strict equality for this ones, always true *) -      | String, Variable String -      | String, Raw String -      | String, Variable NumericString -      | String, Raw NumericString -      | Integer, Variable Integer -      | Integer, Raw Integer -      | NumericString, Variable NumericString -      | NumericString, Raw NumericString -      | Bool, Raw Bool -      | Bool, Variable Bool -      (* Also include the conversion between bool and integer *) -      | Integer, Raw Bool -      | Integer, Variable Bool -      (* The type NumericString can be used as a generic type in input *) -      | _, Variable NumericString -      | NumericString, Raw String -      | NumericString, Variable String -      | NumericString, Raw Integer -      | NumericString, Variable Integer -> -          true -      | Bool, Variable Integer -      | Bool, Raw Integer -      | String, Variable Integer -      | String, Raw Bool -      | String, Variable Bool -      | Integer, Variable String -      | Integer, Raw NumericString -> -          not strict -      (* Explicit rejected cases  *) -      | String, Raw Integer | Integer, Raw String -> false -      | _, _ -> false -    in -    if equal then report -    else -      let result_type = match actual.t with Variable v -> v | Raw r -> r in -      let message = -        Format.asprintf "The type %a is expected but got %a" Get_type.pp_type_of -          expected Get_type.pp_type_of result_type -      in -      Report.message level actual.pos message :: report - -  let rec compare_parameter : -      strict:bool -> -      ?level:Report.level -> -      argument -> -      argument_repr -> -      Report.t list -> -      Report.t list = -   fun ~strict ?(level = Report.Warn) expected param report -> -    match expected with -    | Fixed t -> compare ~strict ~level t param report -    | Dynamic d -> -        let type_ = match d param.t with Raw r -> r | Variable v -> v in -        compare ~strict ~level type_ param report -    | Variable c -> compare_parameter ~level ~strict c param report - -  (** Compare the arguments one by one *) -  let compare_args : -      ?strict:bool -> -      ?level:Report.level -> -      S.pos -> -      argument list -> -      argument_repr list -> -      Report.t list -> -      Report.t list = -   fun ?(strict = false) ?(level = Report.Warn) pos expected actuals report -> -    let tl, report = -      List.fold_left actuals ~init:(expected, report) -        ~f:(fun (expected, report) param -> -          match expected with -          | (Variable _ as hd) :: _ -> -              let check = compare_parameter ~strict ~level hd param report in -              (expected, check) -          | hd :: tl -> -              let check = compare_parameter ~strict ~level hd param report in -              (tl, check) -          | [] -> -              let msg = Report.error param.pos "Unexpected argument" in -              ([], msg :: report)) -    in -    match tl with -    | [] | Variable _ :: _ -> report -    | _ -> -        let msg = Report.error pos "Not enougth arguments given" in -        msg :: report -end - -module TypeBuilder = Compose.Expression (Get_type) - -type t' = { result : Get_type.t Lazy.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 } - -module TypedExpression = struct -  type nonrec t' = t' * Report.t list -  type state = { pos : S.pos } -  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) - -  (** 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 -> -    (* Extract the error from the index *) -    let report = -      match var.index with -      | None -> [] -      | Some (_, expr) -> -          let _, r = expr in -          r -    in -    ({ pos = var.pos }, report) - -  let integer : S.pos -> string -> Get_type.t Lazy.t -> t = -   fun pos value _type_of -> -    let int_value = int_of_string_opt value in - -    let report = -      match int_value with -      | Some 0 -> [] -      | Some _ -> [] -      | None -> Report.error pos "Invalid integer value" :: [] -    in - -    ({ pos }, 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; -    let init = [] in -    let report = -      List.fold_left values ~init ~f:(fun report -> function -        | T.Text _ -> report -        | T.Expression (_, t) -> -            let report = List.rev_append (snd t) report in -            report) -    in -    ({ pos }, 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 -> -    (* 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; -          let t, r = param in -          let arg = arg_of_repr type_of t.pos in -          (arg :: types, r @ report)) -    in -    let types = List.rev types and default = { pos } in - -    match function_ with -    | Arrcomp | Arrpos | Arrsize | Countobj | Desc | Dyneval | Getobj | Instr -    | Isplay -> -        (default, report) -    | Desc' | Dyneval' | Getobj' -> (default, report) -    | Func | Func' -> (default, report) -    | Iif | Iif' -> -        let d = Helper.DynType.t () in -        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) -    | Input | Input' -> -        (* Input should check the result if the variable is a num and raise a -           message in this case.*) -        let expected = Helper.[ Fixed String ] in -        let report = Helper.compare_args pos expected types report in -        (default, report) -    | Isnum -> -        let expected = Helper.[ Fixed String ] in -        let report = Helper.compare_args pos expected types report in -        (default, report) -    | Lcase | Lcase' | Ucase | Ucase' -> -        let expected = Helper.[ Fixed String ] in -        let report = Helper.compare_args pos expected types report in -        (default, report) -    | Len -> -        let expected = Helper.[ Fixed NumericString ] in -        let report = Helper.compare_args pos expected types report in -        (default, report) -    | Loc -> -        let expected = Helper.[ Fixed String ] in -        let report = Helper.compare_args pos expected types report in -        (default, report) -    | Max | Max' | Min | Min' -> -        let d = Helper.DynType.t () in -        (* 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) -    | Mid | Mid' -> -        let expected = Helper.[ Fixed String; Variable (Fixed Integer) ] in -        let report = Helper.compare_args pos expected types report in -        (default, report) -    | Msecscount -> (default, report) -    | Rand -> -        let expected = Helper.[ Variable (Fixed Integer) ] in -        let report = Helper.compare_args pos expected types report in -        (default, report) -    | Replace -> (default, report) -    | Replace' -> (default, report) -    | Rgb -> (default, report) -    | Rnd -> -        (* No arg *) -        let report = Helper.compare_args pos [] types report in -        (default, report) -    | Selact -> (default, report) -    | Str | Str' -> -        let expected = Helper.[ Variable (Fixed Integer) ] in -        let report = Helper.compare_args pos expected types report in -        (default, report) -    | Strcomp -> (default, report) -    | Strfind -> (default, report) -    | Strfind' -> (default, report) -    | Strpos -> (default, report) -    | Trim -> (default, report) -    | Trim' -> (default, report) -    | Val -> -        let expected = Helper.[ Fixed NumericString ] in -        let report = Helper.compare_args pos expected types report in -        (default, report) - -  (** 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 -    match operator with -    | Add -> (t, report) -    | Neg | No -> -        let types = [ arg_of_repr type_of t.pos ] in -        let expected = Helper.[ Fixed Integer ] in -        let report = Helper.compare_args pos expected types report in -        ({ pos }, 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; -    let t1, report1 = t1 in -    let t2, report2 = t2 in - -    let report = report1 @ report2 in - -    let types = [ arg_of_repr type_1 t1.pos; arg_of_repr type_2 t2.pos ] in - -    match operator with -    | T.Plus -> -        (* We cannot really much here, because the (+) function can be used to -           concatenate string or add numbers. - -           When concatenating, it’s allowed to add an integer and a number. -        *) -        ({ pos }, 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 *) -        let d = Helper.(Dynamic (DynType.t ())) in -        let expected = [ d; d ] in -        (* Compare and report as error if the types are incompatible. If no -           error is reported, try in strict mode, and report as a warning. *) -        let report = -          match -            Helper.compare_args ~level:Error pos expected (List.rev types) -              report -          with -          | [] -> -              Helper.compare_args ~strict:true pos expected (List.rev types) -                report -          | report -> report -        in -        ({ pos }, 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) -    | 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) -end - -module Expression = 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 - -  type expression = Expression.t' - -  (** Call for an instruction like [GT] or [*CLR] *) -  let call : S.pos -> T.keywords -> expression list -> t = -   fun _pos _ expressions -> -    List.fold_left expressions ~init:[] ~f:(fun acc a -> -        let _, report = a in -        (List.rev_append report) acc) - -  let location : S.pos -> string -> t = fun _pos _ -> [] - -  (** Comment *) -  let comment : S.pos -> t = fun _pos -> [] - -  (** Raw expression *) -  let expression : expression -> t = fun expression -> snd expression - -  (** Helper function used in the [if_] function. *) -  let fold_clause : t -> (expression, t) S.clause -> t = -   fun report (_pos, expr, instructions) -> -    let result, r = expr in - -    let r2 = -      Helper.compare ~strict:false Get_type.Bool -        (arg_of_repr result.result result.pos) -        [] -    in - -    List.fold_left instructions -      ~init:(r @ r2 @ report) -      ~f:(fun acc a -> -        let report = a in -        (List.rev_append report) acc) - -  let if_ : -      S.pos -> -      (expression, t) S.clause -> -      elifs:(expression, t) S.clause list -> -      else_:(S.pos * t list) option -> -      t = -   fun _pos clause ~elifs ~else_ -> -    (* Traverse the whole block recursively *) -    let report = fold_clause [] clause in -    let report = List.fold_left elifs ~f:fold_clause ~init:report in - -    match else_ with -    | None -> report -    | Some (_, instructions) -> -        List.fold_left instructions ~init:report ~f:(fun acc a -> -            let report = a in -            (List.rev_append report) acc) - -  let act : S.pos -> label:expression -> t list -> t = -   fun _pos ~label instructions -> -    let result, report = label in -    let report = -      Helper.compare ~strict:false Get_type.String -        (arg_of_repr result.result result.pos) -        report -    in - -    List.fold_left instructions ~init:report ~f:(fun acc a -> -        let report = a in -        (List.rev_append report) acc) - -  let assign : -      S.pos -> -      (S.pos, expression) S.variable -> -      T.assignation_operator -> -      expression -> -      t = -   fun pos variable op expression -> -    let right_expression, report = expression in - -    let report' = Option.map snd variable.index |> Option.value ~default:[] in - -    let report = List.rev_append report' report in - -    match (op, Get_type.get_type (Lazy.force 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 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 op1 = arg_of_repr var_type variable.pos in -        let op2 = arg_of_repr right_expression.result right_expression.pos in - -        let d = Helper.DynType.t () in -        (* Every part of the assignation should be the same type *) -        let expected = Helper.[ Dynamic d; Dynamic d ] in - -        match -          Helper.compare_args ~strict:false ~level:Report.Error pos expected -            [ op1; op2 ] [] -        with -        | [] -> -            Helper.compare_args ~strict:true ~level:Report.Warn pos expected -              [ op1; op2 ] report -        | reports -> reports @ report) -end - -module Location = struct -  type t = Report.t list -  type instruction = Instruction.t' - -  let v = Fun.id - -  let location : unit -> S.pos -> instruction list -> t = -   fun () _pos instructions -> -    let report = -      List.fold_left instructions ~init:[] ~f:(fun report instruction -> -          let report' = instruction in -          report' @ report) -    in -    report -end diff --git a/lib/syntax/type_of.mli b/lib/syntax/type_of.mli deleted file mode 100644 index 551f9ac..0000000 --- a/lib/syntax/type_of.mli +++ /dev/null @@ -1,7 +0,0 @@ -include S.Analyzer -(** The module [type_of] populate the report with differents inconsistency -   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. *) diff --git a/lib/syntax/write_only.ml b/lib/syntax/write_only.ml deleted file mode 100644 index ec2e368..0000000 --- a/lib/syntax/write_only.ml +++ /dev/null @@ -1,217 +0,0 @@ -(** Check all the write_only variables *) - -open StdLabels - -(** Identifier for the module *) -let identifier = "write_only" - -(** Short description*) -let description = "Check variables never read" - -(** Is the test active or not *) -let active = ref false - -let is_global = true - -module Key = struct -  type t = string - -  let equal = String.equal -  let hash = Hashtbl.hash -  let compare = String.compare -end - -module StringMap = Hashtbl.Make (Key) -module Set = Set.Make (Key) - -type data = { write : bool; read : bool; position : S.pos list } -type context = (string * data) StringMap.t - -let initialize () = StringMap.create 16 - -let keywords = -  [ -    "BACKIMAGE"; -    "$BACKIMAGE"; -    "BCOLOR"; -    "DEBUG"; -    "DISABLESCROLL"; -    "DISABLESUBEX"; -    "FCOLOR"; -    "$FNAME"; -    "FSIZE"; -    "GC"; -    "LCOLOR"; -    "NOSAVE"; -  ] -  |> Set.of_list - -let set_readed : -    ?update_only:bool -> S.pos -> string -> string -> context -> unit = - fun ?(update_only = false) pos identifier filename map -> -  if not (Set.mem identifier keywords) then -    match (update_only, StringMap.find_opt map identifier) with -    | false, None -> -        StringMap.add map identifier -          (filename, { write = false; read = true; position = [] }) -    | _, Some (filename, v) -> -        StringMap.replace map identifier -          (filename, { v with read = true; position = pos :: v.position }) -    | true, None -> () - -let set_write : S.pos -> string -> string -> context -> unit = - fun pos identifier filename map -> -  if not (Set.mem identifier keywords) then -    match StringMap.find_opt map identifier with -    | None -> -        StringMap.add map identifier -          (filename, { write = true; read = false; position = pos :: [] }) -    | Some (filename, v) -> -        StringMap.replace map identifier -          (filename, { v with write = true; position = pos :: v.position }) - -module Expression = struct -  type t = string -> context -> unit - -  let v : t -> t = Fun.id - -  include Default.Expression (struct -    type nonrec t = t - -    let default _ map = ignore map -  end) - -  let ident : (S.pos, t) S.variable -> t = -   fun variable filename map -> -    (* 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 -> -    List.iter l ~f:(function -      | T.Text t -> -          set_readed pos ~update_only:true (String.uppercase_ascii t) filename -            map -      | T.Expression exprs -> -          (* 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 uoperator : S.pos -> T.uoperator -> t -> t = fun _ _ t map -> t map - -  let boperator : S.pos -> T.boperator -> t -> t -> t = -   fun _ _ t1 t2 filename map -> -    t1 filename map; -    t2 filename map -end - -module Instruction = struct -  type t = Expression.t -  (** Internal type used in the evaluation *) - -  type t' = t - -  let v : t -> t' = Fun.id - -  type expression = Expression.t - -  let location : S.pos -> string -> t = fun _pos _ _ _ -> () - -  let call : S.pos -> T.keywords -> expression list -> t = -   fun _ op exprs filename map -> -    match op with -    | T.KillVar -> -        (* Killing a variable does not count as reading it *) -        () -    | _ -> List.iter ~f:(fun v -> v filename map) exprs - -  let comment : S.pos -> t = fun _ _ _ -> () -  let expression : expression -> t = fun expression map -> expression map - -  let fold_clause : (expression, t) S.clause -> t = -   fun clause filename map -> -    let _, expr, exprs = clause in -    let () = expr filename map in -    let () = List.iter ~f:(fun v -> v filename map) exprs in -    () - -  let if_ : -      S.pos -> -      (expression, t) S.clause -> -      elifs:(expression, t) S.clause list -> -      else_:(S.pos * t list) option -> -      t = -   fun pos clauses ~elifs ~else_ filename map -> -    ignore pos; -    let () = fold_clause clauses filename map in -    let () = List.iter ~f:(fun v -> fold_clause v filename map) elifs in -    Option.iter -      (fun (_, exprs) -> List.iter exprs ~f:(fun v -> v filename map)) -      else_; -    () - -  let act : S.pos -> label:expression -> t list -> t = -   fun pos ~label exprs filename map -> -    ignore pos; -    ignore label; -    List.iter ~f:(fun v -> v filename map) exprs - -  let assign : -      S.pos -> -      (S.pos, expression) S.variable -> -      T.assignation_operator -> -      expression -> -      t = -   fun pos variable op expr filename map -> -    ignore op; -    ignore expr; -    Option.iter (fun v -> v filename map) variable.index; -    expr filename map; -    set_write pos variable.name filename map -end - -module Location = struct -  type t = unit -  type instruction = string -> context -> unit - -  let v : t -> Report.t list = fun _ -> [] - -  let location : context -> S.pos -> instruction list -> t = -   fun context pos instructions -> -    let file_name = (snd pos).Lexing.pos_fname in -    ignore pos; -    ignore context; -    let () = List.iter ~f:(fun v -> v file_name context) instructions in -    () -end - -(** Extract the results from the whole parsing *) -let finalize : context -> (string * Report.t) list = - fun map -> -  let () = -    StringMap.filter_map_inplace -      (fun _ (loc, value) -> -        match value.read && value.write with -        | true -> None -        | false -> Some (loc, value)) -      map -  in - -  let report = -    StringMap.fold -      (fun ident (loc, value) report -> -        match value.read with -        | false -> -            List.fold_left value.position ~init:report ~f:(fun report pos -> -                let msg = -                  Report.debug pos -                    (String.concat ~sep:" " -                       [ "The variable"; ident; "is never read" ]) -                in -                (loc, msg) :: report) -        | true -> report) -      map [] -  in -  report  | 
