aboutsummaryrefslogtreecommitdiff
path: root/syntax/type_of.ml
diff options
context:
space:
mode:
Diffstat (limited to 'syntax/type_of.ml')
-rw-r--r--syntax/type_of.ml348
1 files changed, 348 insertions, 0 deletions
diff --git a/syntax/type_of.ml b/syntax/type_of.ml
new file mode 100644
index 0000000..5ef90f4
--- /dev/null
+++ b/syntax/type_of.ml
@@ -0,0 +1,348 @@
+open StdLabels
+
+type pos = Lexing.position * Lexing.position
+(** Extract the type for expression *)
+
+module Helper = struct
+ type t = Integer | Bool | String | Any
+ [@@deriving show { with_path = false }]
+
+ type dyn_type = t -> 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 dyn_type : unit -> dyn_type =
+ fun () ->
+ let stored = ref None in
+ fun t ->
+ match !stored with
+ | None ->
+ stored := Some t;
+ t
+ | Some t -> t
+
+ (** 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 t | Dynamic of dyn_type | Variable of argument
+
+ let compare :
+ ?level:Report.level -> t -> pos * t -> Report.t list -> Report.t list =
+ fun ?(level = Report.Warn) expected actual report ->
+ let equal =
+ match (expected, snd actual) with
+ | _, Any -> true
+ | Any, _ -> true
+ | String, String -> true
+ | Integer, Integer -> true
+ | Bool, Bool -> true
+ | Bool, Integer -> true
+ | Integer, Bool -> true
+ | String, Integer -> true
+ | String, Bool -> true
+ | _, String -> false
+ in
+ if equal then report
+ else
+ let message =
+ Format.asprintf "The type %a is expected but got %a" pp expected pp
+ (snd actual)
+ in
+ Report.message level (fst actual) message :: report
+
+ let rec compare_parameter :
+ ?level:Report.level ->
+ argument ->
+ pos * t ->
+ Report.t list ->
+ Report.t list =
+ fun ?(level = Report.Warn) expected param report ->
+ match expected with
+ | Fixed t -> compare ~level t param report
+ | Dynamic d ->
+ let type_ = d (snd param) in
+ compare ~level type_ param report
+ | Variable c -> compare_parameter ~level c param report
+
+ (** Compare the arguments one by one *)
+ let compare_args :
+ ?level:Report.level ->
+ pos ->
+ argument list ->
+ (pos * t) list ->
+ Report.t list ->
+ Report.t list =
+ fun ?(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 ~level hd param report in
+ (expected, check)
+ | hd :: tl ->
+ let check = compare_parameter ~level hd param report in
+ (tl, check)
+ | [] ->
+ let msg = Report.error (fst param) "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 Expression = struct
+ type 'a obs
+
+ type t = {
+ result : Helper.t;
+ report : Report.t list;
+ pos : pos;
+ empty : bool;
+ }
+
+ type repr = Report.t list -> t
+ (** The type repr is a function accepting the report as a first argement.
+ When the report is given, it will be reported into the tree and collected
+ in bottom-top *)
+
+ type variable = { pos : pos; name : string; index : repr option }
+
+ let arg_of_repr : t -> pos * Helper.t =
+ fun { result; report; pos; empty } ->
+ ignore report;
+ ignore empty;
+ (pos, result)
+
+ (** The variable has type string when starting with a '$' *)
+ let ident : variable -> repr =
+ fun var report ->
+ let empty = false in
+ match var.name.[0] with
+ | '$' -> { result = String; report; pos = var.pos; empty }
+ | _ -> { result = Integer; report; pos = var.pos; empty }
+
+ let integer : pos -> string -> repr =
+ fun pos value report ->
+ let empty =
+ match int_of_string_opt value with Some 0 -> true | _ -> false
+ in
+
+ { result = Integer; report; pos; empty }
+
+ let literal : pos -> string -> repr =
+ fun pos value report ->
+ let empty = String.equal String.empty value in
+ { result = String; report; pos; empty }
+
+ let function_ : pos -> T.function_ -> repr list -> repr =
+ fun pos function_ params _acc ->
+ (* 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:([], _acc) ~f:(fun (types, report) param ->
+ let arg = arg_of_repr (param report) in
+ (arg :: types, report))
+ in
+ let types = List.rev types
+ and default = { result = Any; report; pos; empty = false } in
+
+ match function_ with
+ | Arrcomp | Arrpos | Arrsize | Countobj | Desc | Dyneval | Func | Getobj
+ | Instr | Isplay ->
+ { default with result = Integer }
+ | Desc' | Dyneval' | Func' | Getobj' -> { default with result = String }
+ | Iif | Iif' ->
+ let d = Helper.dyn_type () 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 *)
+ let result = d Helper.Bool in
+ { result; report; pos; empty = false }
+ | 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
+ { result = String; report; pos; empty = false }
+ | Isnum ->
+ let expected = Helper.[ Fixed String ] in
+ let report = Helper.compare_args pos expected types report in
+ { result = Bool; report; pos; empty = false }
+ | Lcase | Lcase' | Ucase | Ucase' ->
+ let expected = Helper.[ Fixed String ] in
+ let report = Helper.compare_args pos expected types report in
+ { result = String; report; pos; empty = false }
+ | Len ->
+ let expected = Helper.[ Fixed Any ] in
+ let report = Helper.compare_args pos expected types report in
+ { result = Integer; report; pos; empty = false }
+ | Loc ->
+ let expected = Helper.[ Fixed String ] in
+ let report = Helper.compare_args pos expected types report in
+ { result = Bool; report; pos; empty = false }
+ | Max | Max' | Min | Min' ->
+ let d = Helper.dyn_type () 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
+ let result = d Helper.Bool in
+ { result; report; pos; empty = false }
+ | Mid | Mid' ->
+ let expected = Helper.[ Fixed String; Variable (Fixed Integer) ] in
+ let report = Helper.compare_args pos expected types report in
+ { result = String; report; pos; empty = false }
+ | Msecscount -> { result = Integer; report; pos; empty = false }
+ | Rand ->
+ let expected = Helper.[ Variable (Fixed Integer) ] in
+ let report = Helper.compare_args pos expected types report in
+ { result = Integer; report; pos; empty = false }
+ | Replace -> { result = Integer; report; pos; empty = false }
+ | Replace' -> { result = String; report; pos; empty = false }
+ | Rgb -> { result = Integer; report; pos; empty = false }
+ | Qspver | Qspver' | Rnd ->
+ (* No arg *)
+ let report = Helper.compare_args pos [] types report in
+ { result = Integer; report; pos; empty = false }
+ | Selact -> { result = Integer; report; pos; empty = false }
+ | Stattxt -> { result = Integer; report; pos; empty = false }
+ | Stattxt' -> { result = String; report; pos; empty = false }
+ | Str | Str' ->
+ let expected = Helper.[ Variable (Fixed Integer) ] in
+ let report = Helper.compare_args pos expected types report in
+ { default with result = String; report }
+ | Strcomp -> { result = Integer; report; pos; empty = false }
+ | Strfind -> { result = Integer; report; pos; empty = false }
+ | Strfind' -> { result = String; report; pos; empty = false }
+ | Strpos -> { result = Integer; report; pos; empty = false }
+ | Trim -> { result = Integer; report; pos; empty = false }
+ | Trim' -> { result = String; report; pos; empty = false }
+ | Val ->
+ let expected = Helper.[ Fixed Any ] in
+ let report = Helper.compare_args pos expected types report in
+ { result = Integer; report; pos; empty = false }
+
+ (** Unary operator like [-123] or [+'Text']*)
+ let uoperator : pos -> T.uoperator -> repr -> repr =
+ fun pos operator t1 report ->
+ let t = t1 report in
+ match operator with
+ | Add -> t
+ | Neg | No ->
+ let types = [ arg_of_repr t ] in
+ let expected = Helper.[ Fixed Integer ] in
+ let report = Helper.compare_args pos expected types report in
+ { result = Integer; report; pos; empty = false }
+
+ let boperator : pos -> T.boperator -> repr -> repr -> repr =
+ fun pos operator t1 t2 report ->
+ let types = [ arg_of_repr (t1 report); arg_of_repr (t2 report) ] in
+ match operator with
+ | T.Plus ->
+ (* Operation over number *)
+ let d = Helper.(Dynamic (dyn_type ())) in
+ let expected = [ d; d ] in
+ let report = Helper.compare_args pos expected types report in
+ { result = Bool; report; pos; empty = false }
+ | T.Eq | T.Neq | Lt | Gte | Lte | Gt ->
+ let d = Helper.(Dynamic (dyn_type ())) in
+ let expected = [ d; d ] in
+ let report = Helper.compare_args pos expected types report in
+ { result = Bool; report; pos; empty = false }
+ | 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
+ { result = Integer; report; pos; empty = false }
+ | 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
+ { result = Bool; report; pos; empty = false }
+end
+
+module Instruction = struct
+ type repr = Report.t list -> Report.t list
+ type expression = Expression.repr
+ type variable = Expression.variable
+
+ (** Call for an instruction like [GT] or [*CLR] *)
+ let call : pos -> string -> expression list -> repr =
+ fun _pos _ _ report -> report
+
+ let location : pos -> string -> repr = fun _pos _ report -> report
+
+ (** Comment *)
+ let comment : pos -> repr = fun _pos report -> report
+
+ (** Raw expression *)
+ let expression : expression -> repr =
+ fun expression report -> (expression report).report
+
+ type clause = pos * expression * repr list
+
+ let if_ : pos -> clause -> elifs:clause list -> else_:repr list -> repr =
+ fun _pos clause ~elifs ~else_ report ->
+ (* Helper function *)
+ let fold_clause report (_pos, expr, instructions) : Report.t list =
+ let result = expr report in
+ let report =
+ Helper.compare Helper.Bool
+ (Expression.arg_of_repr result)
+ result.Expression.report
+ in
+ List.fold_left instructions ~init:report ~f:(fun report instruction ->
+ instruction report)
+ in
+
+ (* Traverse the whole block recursively *)
+ let report = fold_clause report clause in
+ let report = List.fold_left elifs ~f:fold_clause ~init:report in
+ List.fold_left else_ ~init:report ~f:(fun report instruction ->
+ instruction report)
+
+ let act : pos -> label:expression -> repr list -> repr =
+ fun _pos ~label instructions report ->
+ let result = label report in
+ let report =
+ Helper.compare Helper.String
+ (Expression.arg_of_repr result)
+ result.Expression.report
+ in
+ List.fold_left instructions ~init:report ~f:(fun report instruction ->
+ instruction report)
+
+ let assign : pos -> variable -> T.assignation_operator -> expression -> repr =
+ fun pos variable _ expression report ->
+ let right_expression = expression report in
+ match right_expression.empty with
+ | true -> report
+ | false ->
+ let op1 = Expression.arg_of_repr (Expression.ident variable report) in
+ let report = right_expression.Expression.report in
+ let op2 = Expression.arg_of_repr right_expression in
+
+ let d = Helper.dyn_type () in
+ (* Every part of the assignation should be the same type *)
+ let expected = Helper.[ Dynamic d; Dynamic d ] in
+ Helper.compare_args ~level:Report.Debug pos expected [ op1; op2 ] report
+end
+
+module Location = struct
+ type repr = Instruction.repr
+ type instruction = Instruction.repr
+
+ let location : pos -> instruction list -> repr =
+ fun _pos instructions report ->
+ List.fold_left instructions ~init:report ~f:(fun report instruction ->
+ instruction report)
+end