diff options
Diffstat (limited to 'syntax/type_of.ml')
-rw-r--r-- | syntax/type_of.ml | 348 |
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 |