diff --git a/OCamlPrintf/README.md b/OCamlPrintf/README.md index 04a5b9602..1c7d056d6 100644 --- a/OCamlPrintf/README.md +++ b/OCamlPrintf/README.md @@ -31,7 +31,12 @@ - [x] Auto - [x] Shrinker - [x] Type Checker -- [ ] Interpreter +- [x] Interpreter +- [ ] Support for char, string types and operations with them + - [x] Types + - [x] Comparison + - [x] Concatenation +- [ ] Support the formatted printing function ## Build @@ -46,6 +51,8 @@ dune build # Build the project. dune runtest # Run all tests. dune exec -- repl/REPL.exe -dparsetree -fromfile tests/factorial.txt # Run parser tests and see AST. dune exec -- tests/run_qchecker.exe -v # Run qchecker tests with verbose mode. +dune exec -- repl/REPL.exe -inference # Run inferencer in REPL. +dune exec repl/REPL.exe # Run interpreter. ``` ## Authors diff --git a/OCamlPrintf/lib/ast.ml b/OCamlPrintf/lib/ast.ml index afdd83f75..f3db03265 100644 --- a/OCamlPrintf/lib/ast.ml +++ b/OCamlPrintf/lib/ast.ml @@ -24,14 +24,15 @@ let bin_op_list = ; "/", 1 ; "+", 2 ; "-", 2 - ; ">=", 3 - ; "<=", 3 - ; "<>", 3 - ; "=", 3 - ; ">", 3 - ; "<", 3 - ; "&&", 4 - ; "||", 5 + ; "^", 3 + ; ">=", 4 + ; "<=", 4 + ; "<>", 4 + ; "=", 4 + ; ">", 4 + ; "<", 4 + ; "&&", 5 + ; "||", 6 ] ;; diff --git a/OCamlPrintf/lib/inferencer.ml b/OCamlPrintf/lib/inferencer.ml index cba18d78c..3c43950ab 100644 --- a/OCamlPrintf/lib/inferencer.ml +++ b/OCamlPrintf/lib/inferencer.ml @@ -114,9 +114,8 @@ let pp_scheme ppf = function module Type = struct let rec occurs_in var = function - | Type_option ty -> occurs_in var ty + | Type_option ty | Type_list ty -> occurs_in var ty | Type_var name -> name = var - | Type_list ty -> occurs_in var ty | Type_tuple (fst_ty, snd_ty, ty_list) -> List.exists (occurs_in var) (fst_ty :: snd_ty :: ty_list) | Type_arrow (l, r) -> occurs_in var l || occurs_in var r @@ -125,11 +124,10 @@ module Type = struct let free_vars = let rec helper acc = function - | Type_option ty -> helper acc ty + | Type_option ty | Type_list ty -> helper acc ty | Type_var name -> VarSet.add name acc | Type_tuple (fst_ty, snd_ty, ty_list) -> List.fold_left helper acc (fst_ty :: snd_ty :: ty_list) - | Type_list ty -> helper acc ty | Type_arrow (l, r) -> helper (helper acc l) r | _ -> acc in @@ -473,7 +471,7 @@ module Infer = struct extend_env_with_bind_names env (value_binding :: value_binding_list) in let* env, sub1 = - rec_infer_value_binding_list + infer_rec_value_binding_list env fresh_acc Subst.empty @@ -496,6 +494,7 @@ module Infer = struct let* required_arg_ty, required_result_ty = match opr with | "*" | "/" | "+" | "-" -> return (Type_int, Type_int) + | "^" -> return (Type_string, Type_string) | ">=" | "<=" | "<>" | "=" | ">" | "<" -> let* fresh = fresh_var in return (fresh, Type_bool) @@ -702,8 +701,8 @@ module Infer = struct let* new_sub, ty = infer_expression env exp in infer_vb new_sub env ty pat rest - and rec_infer_value_binding_list ?(debug = false) env fresh_acc sub let_binds = - let rec_infer_vb new_sub fresh ty id fresh_acc rest ~required_ty = + and infer_rec_value_binding_list ?(debug = false) env fresh_acc sub let_binds = + let infer_rec_vb new_sub fresh ty id fresh_acc rest ~required_ty = let* new_sub = match required_ty with | Some c_ty -> @@ -720,41 +719,41 @@ module Infer = struct in if debug then pp_scheme Format.std_formatter generalized_ty; let env = TypeEnv.extend env id generalized_ty in - rec_infer_value_binding_list ~debug env fresh_acc composed_sub rest + infer_rec_value_binding_list ~debug env fresh_acc composed_sub rest in match let_binds, fresh_acc with | [], _ -> return (env, sub) | ( { pat = Pat_var id; exp = (Exp_fun _ | Exp_function _) as exp } :: rest , fresh :: fresh_acc ) -> let* new_sub, ty = infer_expression env exp in - rec_infer_vb new_sub fresh ty id fresh_acc rest ~required_ty:None + infer_rec_vb new_sub fresh ty id fresh_acc rest ~required_ty:None | ( { pat = Pat_constraint (Pat_var id, pat_ty); exp = Exp_fun (pat, pat_list, exp) } :: rest , fresh :: fresh_acc ) -> let* new_sub, ty = infer_expression env (Exp_fun (pat, pat_list, Exp_constraint (exp, pat_ty))) in - rec_infer_vb new_sub fresh ty id fresh_acc rest ~required_ty:None + infer_rec_vb new_sub fresh ty id fresh_acc rest ~required_ty:None | ( { pat = Pat_constraint (Pat_var id, pat_ty); exp = Exp_function _ as exp } :: rest , fresh :: fresh_acc ) -> let* new_sub, ty = infer_expression env (Exp_constraint (exp, pat_ty)) in - rec_infer_vb new_sub fresh ty id fresh_acc rest ~required_ty:None + infer_rec_vb new_sub fresh ty id fresh_acc rest ~required_ty:None | { pat = Pat_var id; exp } :: rest, fresh :: fresh_acc -> let* new_sub, ty = infer_expression env exp in let update_fresh = Subst.apply new_sub fresh in if ty = update_fresh then fail `No_arg_rec - else rec_infer_vb new_sub fresh ty id fresh_acc rest ~required_ty:None + else infer_rec_vb new_sub fresh ty id fresh_acc rest ~required_ty:None | { pat = Pat_constraint (Pat_var id, pat_ty); exp } :: rest, fresh :: fresh_acc -> let* new_sub, ty = infer_expression env exp in let update_fresh = Subst.apply new_sub fresh in if ty = update_fresh then fail `No_arg_rec - else rec_infer_vb new_sub fresh ty id fresh_acc rest ~required_ty:(Some pat_ty) + else infer_rec_vb new_sub fresh ty id fresh_acc rest ~required_ty:(Some pat_ty) | _ -> fail `No_variable_rec ;; - let infer_srtucture_item ?(debug = false) env ast = + let infer_structure_item ~debug (env, out_list) = let get_names_from_let_binds env = RList.fold_left ~init:(return []) ~f:(fun acc { pat; _ } -> extract_names_from_pat @@ -762,31 +761,31 @@ module Infer = struct acc pat) in - let* _, out_list = - RList.fold_left - ast - ~init:(return (env, [])) - ~f:(fun (env, out_list) -> - function - | Struct_eval exp -> - let* _, ty = infer_expression env exp in - return (env, out_list @ [ None, ty ]) - | Struct_value (Nonrecursive, value_binding, value_binding_list) -> - let value_binding_list = value_binding :: value_binding_list in - let* _ = check_names_from_let_binds value_binding_list in - let* env, _ = infer_value_binding_list env Subst.empty value_binding_list in - let* id_list = get_names_from_let_binds env value_binding_list in - if debug then TypeEnv.pp Format.std_formatter env; - return (env, out_list @ id_list) - | Struct_value (Recursive, value_binding, value_binding_list) -> - let value_binding_list = value_binding :: value_binding_list in - let* env, fresh_acc = extend_env_with_bind_names env value_binding_list in - let* env, _ = - rec_infer_value_binding_list env fresh_acc Subst.empty value_binding_list - in - let* id_list = get_names_from_let_binds env value_binding_list in - if debug then TypeEnv.pp Format.std_formatter env; - return (env, out_list @ id_list)) + function + | Struct_eval exp -> + let* _, ty = infer_expression env exp in + return (env, out_list @ [ None, ty ]) + | Struct_value (Nonrecursive, value_binding, value_binding_list) -> + let value_binding_list = value_binding :: value_binding_list in + let* _ = check_names_from_let_binds value_binding_list in + let* env, _ = infer_value_binding_list env Subst.empty value_binding_list in + let* id_list = get_names_from_let_binds env value_binding_list in + if debug then TypeEnv.pp Format.std_formatter env; + return (env, out_list @ id_list) + | Struct_value (Recursive, value_binding, value_binding_list) -> + let value_binding_list = value_binding :: value_binding_list in + let* env, fresh_acc = extend_env_with_bind_names env value_binding_list in + let* env, _ = + infer_rec_value_binding_list env fresh_acc Subst.empty value_binding_list + in + let* id_list = get_names_from_let_binds env value_binding_list in + if debug then TypeEnv.pp Format.std_formatter env; + return (env, out_list @ id_list) + ;; + + let infer_srtucture ~debug env ast = + let* env, out_list = + RList.fold_left ast ~init:(return (env, [])) ~f:(infer_structure_item ~debug) in let remove_duplicates = let fun_equal el1 el2 = @@ -799,19 +798,24 @@ module Infer = struct | _ :: xs -> xs | [] -> [] in - return (remove_duplicates out_list) + return (env, remove_duplicates out_list) ;; end let empty_env = TypeEnv.empty -let env_with_print_int = - TypeEnv.extend +let env_with_print_funs = + let print_fun_list = + [ "print_int", Scheme (VarSet.empty, Type_arrow (Type_int, Type_unit)) + ; "print_endline", Scheme (VarSet.empty, Type_arrow (Type_string, Type_unit)) + ] + in + List.fold_left + (fun env (id, sch) -> TypeEnv.extend env id sch) TypeEnv.empty - "print_int" - (Scheme (VarSet.empty, Type_arrow (Type_int, Type_unit))) + print_fun_list ;; -let run_inferencer ?(debug = false) ast env = - State.run (Infer.infer_srtucture_item ~debug env ast) +let run_inferencer ?(debug = false) env ast = + State.run (Infer.infer_srtucture ~debug env ast) ;; diff --git a/OCamlPrintf/lib/inferencer.mli b/OCamlPrintf/lib/inferencer.mli index 117979aee..8c8da30e7 100644 --- a/OCamlPrintf/lib/inferencer.mli +++ b/OCamlPrintf/lib/inferencer.mli @@ -36,10 +36,10 @@ module TypeEnv : sig end val empty_env : TypeEnv.t -val env_with_print_int : TypeEnv.t +val env_with_print_funs : TypeEnv.t val run_inferencer : ?debug:bool - -> Ast.structure_item list -> TypeEnv.t - -> ((Ast.ident option * Ast.core_type) list, error) result + -> Ast.structure + -> (TypeEnv.t * (Ast.ident option * Ast.core_type) list, error) result diff --git a/OCamlPrintf/lib/interpreter.ml b/OCamlPrintf/lib/interpreter.ml new file mode 100644 index 000000000..a5785f803 --- /dev/null +++ b/OCamlPrintf/lib/interpreter.ml @@ -0,0 +1,408 @@ +(** Copyright 2024-2025, Friend-zva, RodionovMaxim05 *) + +(** SPDX-License-Identifier: LGPL-3.0-or-later *) + +open Ast + +type error = + [ `Type_error + | `Division_by_zero + | `Match_failure + | `No_variable of string + ] + +let pp_error ppf : error -> _ = function + | `Type_error -> Format.fprintf ppf "Type error" + | `Division_by_zero -> Format.fprintf ppf "Division by zero" + | `Match_failure -> Format.fprintf ppf "Matching failure" + | `No_variable id -> Format.fprintf ppf "Undefined variable '%s'" id +;; + +type value = + | Val_integer of int + | Val_char of char + | Val_string of string + | Val_fun of rec_flag * pattern * pattern list * Expression.t * env + | Val_function of Expression.t case list * env + | Val_tuple of value * value * value list + | Val_construct of ident * value option + | Val_builtin of string + +and env = (string, value, Base.String.comparator_witness) Base.Map.t + +let rec pp_value ppf = + let open Stdlib.Format in + function + | Val_integer int -> fprintf ppf "%i" int + | Val_char char -> fprintf ppf "'%c'" char + | Val_string str -> fprintf ppf "%S" str + | Val_tuple (fst_val, snd_val, val_list) -> + fprintf + ppf + "(%a)" + (pp_print_list ~pp_sep:(fun ppf () -> fprintf ppf ", ") pp_value) + (fst_val :: snd_val :: val_list) + | Val_fun _ -> fprintf ppf "" + | Val_function _ -> fprintf ppf "" + | Val_construct ("::", Some (Val_tuple (head, tail, []))) -> + fprintf ppf "@[[ %a" pp_value head; + let rec pp_tail = function + | Val_construct (_, None) -> fprintf ppf "@ ]@]" + | Val_construct (_, Some (Val_tuple (next_head, next_tail, []))) -> + fprintf ppf "@,; %a" pp_value next_head; + pp_tail next_tail + | Val_construct (_, Some _) -> () + | value -> fprintf ppf ";@ %a@ ]@]" pp_value value + in + pp_tail tail + | Val_construct (tag, None) -> fprintf ppf "%s" tag + | Val_construct ("Some", Some value) -> fprintf ppf "Some %a" pp_value value + | Val_construct _ -> () + | Val_builtin _ -> fprintf ppf "" +;; + +module Res = struct + open Base + + type 'a t = ('a, error) Result.t + + let fail = Result.fail + let return = Result.return + + let ( >>= ) (monad : 'a t) (f : 'a -> 'b t) : 'b t = + match monad with + | Ok result -> f result + | Error x -> fail x + ;; + + let ( let* ) = ( >>= ) +end + +module EvalEnv = struct + open Base + + let empty = Map.empty (module String) + let extend env key value = Map.update env key ~f:(fun _ -> value) + + let compose env1 env2 = + Map.fold env2 ~f:(fun ~key ~data env_acc -> extend env_acc key data) ~init:env1 + ;; + + let find_exn env key = + match Map.find env key with + | Some value -> Res.return value + | None -> Res.fail (`No_variable key) + ;; + + let find_exn1 env key = + let val' = Map.find_exn env key in + val' + ;; +end + +module Inter = struct + open Ast.Expression + open Res + open EvalEnv + + let eval_arith opr val1 val2 = return (Val_integer (opr val1 val2)) + let eval_concat opr val1 val2 = return (Val_string (opr val1 val2)) + + let eval_eq opr val1 val2 = + return (Val_construct (Bool.to_string (opr val1 val2), None)) + ;; + + let eval_bool opr val1 val2 = + return + (Val_construct + (Bool.to_string (opr (bool_of_string val1) (bool_of_string val2)), None)) + ;; + + let eval_bin_op = function + | "*", Val_integer val1, Val_integer val2 -> eval_arith ( * ) val1 val2 + | "/", Val_integer val1, Val_integer val2 when val2 <> 0 -> eval_arith ( / ) val1 val2 + | "/", _, Val_integer 0 -> fail `Division_by_zero + | "+", Val_integer val1, Val_integer val2 -> eval_arith ( + ) val1 val2 + | "-", Val_integer val1, Val_integer val2 -> eval_arith ( - ) val1 val2 + | "^", Val_string val1, Val_string val2 -> eval_concat ( ^ ) val1 val2 + | ">=", val1, val2 -> eval_eq ( >= ) val1 val2 + | "<=", val1, val2 -> eval_eq ( <= ) val1 val2 + | "<>", val1, val2 -> eval_eq ( <> ) val1 val2 + | "=", val1, val2 -> eval_eq ( = ) val1 val2 + | ">", val1, val2 -> eval_eq ( > ) val1 val2 + | "<", val1, val2 -> eval_eq ( < ) val1 val2 + | "&&", Val_construct (val1, None), Val_construct (val2, None) -> + eval_bool ( && ) val1 val2 + | "||", Val_construct (val1, None), Val_construct (val2, None) -> + eval_bool ( || ) val1 val2 + | _ -> fail `Type_error + ;; + + let rec match_pattern env = function + | Pat_any, _ -> Some env + | Pat_var name, value -> Some (extend env name value) + | Pat_constant (Const_integer pat), Val_integer value when pat = value -> Some env + | Pat_constant (Const_char pat), Val_char value when pat = value -> Some env + | Pat_constant (Const_string pat), Val_string value when pat = value -> Some env + | Pat_tuple (fst_pat, snd_pat, pat_list), Val_tuple (fst_val, snd_val, val_list) -> + let env = + Base.List.fold2 + ~f:(fun env pat value -> + match env with + | Some env -> match_pattern env (pat, value) + | None -> None) + ~init:(Some env) + (fst_pat :: snd_pat :: pat_list) + (fst_val :: snd_val :: val_list) + in + (match env with + | Ok env -> env + | _ -> None) + | ( Pat_construct ("::", Some (Pat_tuple (head_pat, tail_pat, []))) + , Val_construct ("::", Some (Val_tuple (head_val, tail_val, []))) ) -> + let env = match_pattern env (head_pat, head_val) in + (match env with + | Some env -> match_pattern env (tail_pat, tail_val) + | None -> None) + | Pat_construct (id_pat, None), Val_construct (id_val, None) when id_pat = id_val -> + Some env + | Pat_construct ("Some", Some pat), Val_construct ("Some", Some value) -> + match_pattern env (pat, value) + | Pat_constraint (pat, _), value -> match_pattern env (pat, value) + | _ -> None + ;; + + let rec extend_names_from_pat env = function + | (Pat_any | Pat_construct ("()", None)), _ -> return env + | Pat_var id, value -> return (extend env id value) + | Pat_tuple (fst_pat, snd_pat, pat_list), Val_tuple (fst_val, snd_val, val_list) -> + (match + Base.List.fold2 + (fst_pat :: snd_pat :: pat_list) + (fst_val :: snd_val :: val_list) + ~init:(return env) + ~f:(fun acc pat value -> + let* env = acc in + extend_names_from_pat env (pat, value)) + with + | Ok acc -> acc + | _ -> fail `Type_error) + | Pat_construct ("[]", None), Val_construct ("[]", None) -> return env + | ( Pat_construct ("::", Some (Pat_tuple (head_pat, tail_pat, []))) + , Val_construct ("::", Some (Val_tuple (head_val, tail_val, []))) ) -> + let* env = extend_names_from_pat env (head_pat, head_val) in + let* env = extend_names_from_pat env (tail_pat, tail_val) in + return env + | (Pat_construct ("Some", Some pat) | Pat_constraint (pat, _)), value -> + extend_names_from_pat env (pat, value) + | _ -> fail `Type_error + ;; + + let rec eval_expression env = function + | Exp_ident id -> find_exn env id + | Exp_constant const -> + (match const with + | Const_integer int -> return (Val_integer int) + | Const_char char -> return (Val_char char) + | Const_string str -> return (Val_string str)) + | Exp_let (Nonrecursive, value_binding, value_binding_list, exp) -> + let* env = eval_value_binding_list env (value_binding :: value_binding_list) in + eval_expression env exp + | Exp_let (Recursive, value_binding, value_binding_list, exp) -> + let* env = eval_rec_value_binding_list env (value_binding :: value_binding_list) in + eval_expression env exp + | Exp_fun (pat, pat_list, exp) -> + return (Val_fun (Nonrecursive, pat, pat_list, exp, env)) + | Exp_apply (Exp_ident opr, Exp_apply (exp1, exp2)) when is_operator opr -> + let* value1 = eval_expression env exp1 in + let* value2 = eval_expression env exp2 in + eval_bin_op (opr, value1, value2) + | Exp_apply (exp1, exp2) -> + (match exp1 with + | Exp_ident opr when is_negative_op opr -> + let* value = eval_expression env exp2 in + (match value with + | Val_integer value -> return (Val_integer (-value)) + | _ -> fail `Type_error) + | _ -> + let* fun_val = eval_expression env exp1 in + let* arg_val = eval_expression env exp2 in + (match fun_val with + | Val_fun (rec_flag, pat, pat_list, exp, fun_env) -> + let* new_env = + match rec_flag, match_pattern fun_env (pat, arg_val) with + | Recursive, Some extended_env -> return (compose env extended_env) + | Nonrecursive, Some extended_env -> return extended_env + | _, None -> fail `Match_failure + in + (match pat_list with + | [] -> eval_expression new_env exp + | first_pat :: rest_pat_list -> + return (Val_fun (Recursive, first_pat, rest_pat_list, exp, new_env))) + | Val_function (case_list, env) -> find_and_eval_case env arg_val case_list + | Val_builtin builtin -> + (match builtin, arg_val with + | "print_int", Val_integer integer -> + print_int integer; + return (Val_construct ("()", None)) + | "print_endline", Val_string str -> + print_endline str; + return (Val_construct ("()", None)) + | _ -> fail `Type_error) + | _ -> fail `Type_error)) + | Exp_function (case, case_list) -> return (Val_function (case :: case_list, env)) + | Exp_match (exp, case, case_list) -> + let* match_value = eval_expression env exp in + find_and_eval_case env match_value (case :: case_list) + | Exp_tuple (fst_exp, snd_exp, exp_list) -> + let* fst_val = eval_expression env fst_exp in + let* snd_val = eval_expression env snd_exp in + let* val_list = + Base.List.fold_right + ~f:(fun exp acc -> + let* acc = acc in + let* value = eval_expression env exp in + return (value :: acc)) + ~init:(return []) + exp_list + in + return (Val_tuple (fst_val, snd_val, val_list)) + | Exp_construct ("::", Some (Exp_tuple (head, tail, []))) -> + let* val1 = eval_expression env head in + let* val2 = eval_expression env tail in + return (Val_construct ("::", Some (Val_tuple (val1, val2, [])))) + | Exp_construct (id, None) -> return (Val_construct (id, None)) + | Exp_construct ("Some", Some pat) -> + let* value = eval_expression env pat in + return (Val_construct ("Some", Some value)) + | Exp_construct _ -> fail `Type_error + | Exp_ifthenelse (if_exp, then_exp, else_exp) -> + let* if_value = eval_expression env if_exp in + (match if_value with + | Val_construct ("true", None) -> eval_expression env then_exp + | Val_construct ("false", None) -> + Base.Option.value_map + else_exp + ~f:(eval_expression env) + ~default:(return (Val_construct ("()", None))) + | _ -> fail `Type_error) + | Exp_sequence (exp1, exp2) -> + let* _ = eval_expression env exp1 in + let* value = eval_expression env exp2 in + return value + | Exp_constraint (exp, _) -> eval_expression env exp + + and find_and_eval_case env value = function + | [] -> fail `Match_failure + | { left; right } :: tail -> + let env_temp = match_pattern env (left, value) in + (match env_temp with + | Some env -> eval_expression env right + | None -> find_and_eval_case env value tail) + + and eval_value_binding_list env value_binding_list = + Base.List.fold_left + ~f:(fun acc { pat; exp } -> + let* env = acc in + let* value = eval_expression env exp in + match pat with + | Pat_var name | Pat_constraint (Pat_var name, _) -> + let env = extend env name value in + return env + | _ -> + let* env = extend_names_from_pat env (pat, value) in + return env) + ~init:(return env) + value_binding_list + + and eval_rec_value_binding_list env value_binding_list = + Base.List.fold_left + ~f:(fun acc { pat; exp } -> + let* env = acc in + let* value = eval_expression env exp in + match pat with + | Pat_var name | Pat_constraint (Pat_var name, _) -> + let value = + match value with + | Val_fun (_, pat, pat_list, exp, env) -> + Val_fun (Recursive, pat, pat_list, exp, env) + | other -> other + in + let env = extend env name value in + return env + | _ -> fail `Type_error) + ~init:(return env) + value_binding_list + ;; + + let eval_structure_item env out_list = + let rec extract_names_from_pat env acc = function + | Pat_var id -> acc @ [ Some id, EvalEnv.find_exn1 env id ] + | Pat_tuple (fst_pat, snd_pat, pat_list) -> + Base.List.fold_left + (fst_pat :: snd_pat :: pat_list) + ~init:acc + ~f:(extract_names_from_pat env) + | Pat_construct ("::", Some exp) -> + (match exp with + | Pat_tuple (head, tail, []) -> + let acc = extract_names_from_pat env acc head in + extract_names_from_pat env acc tail + | _ -> acc) + | Pat_construct ("Some", Some pat) -> extract_names_from_pat env acc pat + | Pat_constraint (pat, _) -> extract_names_from_pat env acc pat + | _ -> acc + in + let get_names_from_let_binds env = + Base.List.fold_left ~init:[] ~f:(fun acc { pat; _ } -> + extract_names_from_pat env acc pat) + in + function + | Struct_eval exp -> + let* val' = eval_expression env exp in + return (env, out_list @ [ None, val' ]) + | Struct_value (Nonrecursive, value_binding, value_binding_list) -> + let value_binding_list = value_binding :: value_binding_list in + let* env = eval_value_binding_list env value_binding_list in + let eval_list = get_names_from_let_binds env value_binding_list in + return (env, out_list @ eval_list) + | Struct_value (Recursive, value_binding, value_binding_list) -> + let value_binding_list = value_binding :: value_binding_list in + let* env = eval_rec_value_binding_list env value_binding_list in + let eval_list = get_names_from_let_binds env value_binding_list in + return (env, out_list @ eval_list) + ;; + + let eval_structure env ast = + let* env, out_list = + Base.List.fold_left + ~f:(fun acc item -> + let* env, out_list = acc in + let* env, out_list = eval_structure_item env out_list item in + return (env, out_list)) + ~init:(return (env, [])) + ast + in + let remove_duplicates = + let fun_equal el1 el2 = + match el1, el2 with + | (Some id1, _), (Some id2, _) -> String.equal id1 id2 + | _ -> false + in + function + | x :: xs when not (Base.List.mem xs x ~equal:fun_equal) -> x :: xs + | _ :: xs -> xs + | [] -> [] + in + return (env, remove_duplicates out_list) + ;; +end + +let empty_env = EvalEnv.empty + +let env_with_print_funs = + let env = EvalEnv.extend empty_env "print_int" (Val_builtin "print_int") in + EvalEnv.extend env "print_endline" (Val_builtin "print_endline") +;; + +let run_interpreter = Inter.eval_structure diff --git a/OCamlPrintf/lib/interpreter.mli b/OCamlPrintf/lib/interpreter.mli new file mode 100644 index 000000000..2a45d3594 --- /dev/null +++ b/OCamlPrintf/lib/interpreter.mli @@ -0,0 +1,37 @@ +(** Copyright 2024-2025, Friend-zva, RodionovMaxim05 *) + +(** SPDX-License-Identifier: LGPL-3.0-or-later *) + +type error = + [ `Type_error + (** Represents a type error that occurs when a type mismatch is detected in an expression. *) + | `Division_by_zero + (** Represents the error that occurs when attempting to perform a division by zero operation. *) + | `Match_failure + (** Represents a match error occurs when a pattern matching attempt fails. *) + | `No_variable of Ast.ident + (** Represents an error that occurs when attempting to use a variable that has not been declared or initialized. *) + ] + +val pp_error : Format.formatter -> error -> unit + +type value = + | Val_integer of int + | Val_char of char + | Val_string of string + | Val_fun of Ast.rec_flag * Ast.pattern * Ast.pattern list * Ast.Expression.t * env + | Val_function of Ast.Expression.t Ast.case list * env + | Val_tuple of value * value * value list + | Val_construct of Ast.ident * value option + | Val_builtin of Ast.ident + +and env = (Ast.ident, value, Base.String.comparator_witness) Base.Map.t + +val pp_value : Format.formatter -> value -> unit +val empty_env : env +val env_with_print_funs : env + +val run_interpreter + : env + -> Ast.structure + -> (env * (Ast.ident option * value) list, error) result diff --git a/OCamlPrintf/lib/parser.ml b/OCamlPrintf/lib/parser.ml index 30a70f08d..e8e2634c6 100644 --- a/OCamlPrintf/lib/parser.ml +++ b/OCamlPrintf/lib/parser.ml @@ -330,6 +330,7 @@ let parse_operator op_list = let mul_div = parse_operator [ "*"; "/" ] let add_sub = parse_operator [ "+"; "-" ] +let concat = parse_operator [ "^" ] let cmp = parse_operator [ ">="; "<="; "<>"; "="; ">"; "<" ] let and_ = parse_operator [ "&&" ] let or_ = parse_operator [ "||" ] @@ -539,6 +540,7 @@ let parse_exp_apply_un_op parse_exp = let parse_exp_apply_bin_op parse_exp = let parse_exp = parse_left_bin_op parse_exp mul_div in let parse_exp = parse_left_bin_op parse_exp add_sub in + let parse_exp = parse_right_bin_op parse_exp concat in let parse_exp = parse_left_bin_op parse_exp cmp in let parse_exp = parse_right_bin_op parse_exp and_ in parse_right_bin_op parse_exp or_ diff --git a/OCamlPrintf/lib/pprinter.ml b/OCamlPrintf/lib/pprinter.ml index 5eb2848c1..8615b39ec 100644 --- a/OCamlPrintf/lib/pprinter.ml +++ b/OCamlPrintf/lib/pprinter.ml @@ -199,7 +199,7 @@ and pp_exp_apply ?(need_parens = false) ppf (exp1, exp2) = (match exp2 with | Exp_apply (Exp_apply (Exp_ident opr1, exp1), opn) when is_operator opr1 -> (match get_priority exp_opr with - | 4 | 5 -> pp (exp_opr <= opr1) opr1 exp1 + | 3 | 5 | 6 -> pp (exp_opr <= opr1) opr1 exp1 | _ -> pp (exp_opr < opr1) opr1 exp1); fprintf ppf " %s@ " exp_opr; (match opn with @@ -213,7 +213,7 @@ and pp_exp_apply ?(need_parens = false) ppf (exp1, exp2) = | _ -> fprintf ppf "%a" (pp_expression_deep false true) opn); fprintf ppf " %s@ " exp_opr; (match get_priority exp_opr with - | 1 | 2 | 3 -> pp (exp_opr <= opr2) opr2 exp2 + | 1 | 2 | 4 -> pp (exp_opr <= opr2) opr2 exp2 | _ -> pp (exp_opr < opr2) opr2 exp2) | Exp_apply (opn1, opn2) -> fprintf diff --git a/OCamlPrintf/repl/REPL.ml b/OCamlPrintf/repl/REPL.ml index 90c148b8d..5d908c887 100644 --- a/OCamlPrintf/repl/REPL.ml +++ b/OCamlPrintf/repl/REPL.ml @@ -2,48 +2,115 @@ (** SPDX-License-Identifier: LGPL-3.0-or-later *) -open Ocaml_printf_lib.Ast -open Ocaml_printf_lib.Parser -open Ocaml_printf_lib.Inferencer -open Ocaml_printf_lib.Pprinter +open Ocaml_printf_lib open Stdio type opts = { mutable dump_parsetree : bool + ; mutable inference : bool ; mutable input_file : string option } -let run_single dump_parsetree input_source = - let text = - match input_source with - | Some file_name -> In_channel.read_all file_name |> String.trim - | None -> In_channel.input_all stdin |> String.trim +let pp_global_error ppf = function + | #Inferencer.error as e -> Inferencer.pp_error ppf e + | #Interpreter.error as e -> Interpreter.pp_error ppf e +;; + +let run_single dump_parsetree inference input_source = + let run text env_infer env_inter = + let ast = Parser.parse text in + match ast with + | Error error -> + print_endline (Format.asprintf "Parsing error: %s" error); + env_infer, env_inter + | Ok ast -> + if dump_parsetree + then ( + print_endline (Ast.show_structure ast); + env_infer, env_inter) + else ( + match Inferencer.run_inferencer env_infer ast with + | Error e_infer -> + print_endline (Format.asprintf "Inferencer error: %a" pp_global_error e_infer); + env_infer, env_inter + | Ok (env_infer, out_infer_list) -> + if inference + then ( + List.iter + (function + | Some id, type' -> + print_endline + (Format.asprintf "val %s : %a" id Pprinter.pp_core_type type') + | None, type' -> + print_endline (Format.asprintf "- : %a" Pprinter.pp_core_type type')) + out_infer_list; + env_infer, env_inter) + else ( + print_endline "*** Printed ***"; + match Interpreter.run_interpreter env_inter ast with + | Ok (env_inter, out_inter_list) -> + print_endline "\n*** Output ***"; + List.iter2 + (fun (_, val') -> function + | Some id, type' -> + print_endline + (Format.asprintf + "val %s : %a = %a" + id + Pprinter.pp_core_type + type' + Interpreter.pp_value + val') + | None, type' -> + print_endline + (Format.asprintf + "- : %a = %a" + Pprinter.pp_core_type + type' + Interpreter.pp_value + val')) + out_inter_list + out_infer_list; + env_infer, env_inter + | Error e_inter -> + print_endline + (Format.asprintf "Interpreter error: %a" pp_global_error e_inter); + env_infer, env_inter)) + in + let env_infer, env_inter = + Inferencer.env_with_print_funs, Interpreter.env_with_print_funs in - let ast = parse text in - match ast with - | Error error -> print_endline error - | Ok ast -> - if dump_parsetree - then print_endline (show_structure ast) - else ( - match run_inferencer ast env_with_print_int with - | Ok out_list -> - List.iter - (function - | Some id, type' -> Format.printf "val %s : %a\n" id pp_core_type type' - | None, type' -> Format.printf "- : %a\n" pp_core_type type') - out_list - | Error e -> Format.printf "Infer error: %a\n" pp_error e) + match input_source with + | Some file_name -> + let text = In_channel.read_all file_name |> String.trim in + let _, _ = run text env_infer env_inter in + () + | None -> + let rec input_lines lines env_infer env_inter = + match In_channel.input_line stdin with + | Some line -> + if line = ";;" || String.ends_with ~suffix:";;" line + then ( + let env_infer, env_inter = run (lines ^ line) env_infer env_inter in + input_lines "" env_infer env_inter) + else input_lines (lines ^ line) env_infer env_inter + | None -> () + in + let _ = input_lines "" env_infer env_inter in + () ;; let () = - let options = { dump_parsetree = false; input_file = None } in + let options = { dump_parsetree = false; inference = false; input_file = None } in let () = let open Arg in parse [ ( "-dparsetree" , Unit (fun () -> options.dump_parsetree <- true) , "Dump parse tree, don't evaluate anything" ) + ; ( "-inference" + , Unit (fun () -> options.inference <- true) + , "Inference, don't evaluate anything" ) ; ( "-fromfile" , String (fun filename -> options.input_file <- Some filename) , "Read code from the file" ) @@ -53,5 +120,5 @@ let () = exit 1) "Read-Eval-Print-Loop for custom language" in - run_single options.dump_parsetree options.input_file + run_single options.dump_parsetree options.inference options.input_file ;; diff --git a/OCamlPrintf/tests/run_REPL.t b/OCamlPrintf/tests/REPL.t similarity index 50% rename from OCamlPrintf/tests/run_REPL.t rename to OCamlPrintf/tests/REPL.t index 162aec448..e14359358 100644 --- a/OCamlPrintf/tests/run_REPL.t +++ b/OCamlPrintf/tests/REPL.t @@ -30,16 +30,17 @@ SPDX-License-Identifier: LGPL-3.0-or-later ] $ ../repl/REPL.exe -dparsetree < let prime n = - > let rec check_zero x d = - > match d with - > | 1 -> true - > | _ -> x mod d <> 0 && check_zero x (d - 1) - > in - > match n with - > | 0 -> false - > | 1 -> false - > | _ -> check_zero n (n - 1);; + > let prime n = + > let rec check_zero x d = + > match d with + > | 1 -> true + > | _ -> x mod d <> 0 && check_zero x (d - 1) + > in + > match n with + > | 0 -> false + > | 1 -> false + > | _ -> check_zero n (n - 1) + > ;; [(Struct_value (Nonrecursive, { pat = (Pat_var "prime"); exp = @@ -103,103 +104,18 @@ SPDX-License-Identifier: LGPL-3.0-or-later [])) ] - $ ../repl/REPL.exe -fromfile factorial.txt - val factorial : int -> int - - $ ../repl/REPL.exe -fromfile manytests/do_not_type/001.ml - Infer error: Undefined variable 'fac' - - $ ../repl/REPL.exe -fromfile manytests/do_not_type/002if.ml - Infer error: Unification failed on int and bool - - $ ../repl/REPL.exe -fromfile manytests/do_not_type/003occurs.ml - Infer error: Occurs check failed: the type variable 'ty1 occurs inside 'ty1 -> 'ty3 - - $ ../repl/REPL.exe -fromfile manytests/do_not_type/004let_poly.ml - Infer error: Unification failed on int and bool - - $ ../repl/REPL.exe -fromfile manytests/do_not_type/015tuples.ml - Infer error: Only variables are allowed as left-hand side of `let rec' - - $ ../repl/REPL.exe -fromfile manytests/do_not_type/099.ml - Infer error: Only variables are allowed as left-hand side of `let rec' - - $ ../repl/REPL.exe -fromfile manytests/typed/001fac.ml - val fac : int -> int - val main : int - - $ ../repl/REPL.exe -fromfile manytests/typed/002fac.ml - val fac_cps : int -> (int -> 'a) -> 'a - val main : int - - $ ../repl/REPL.exe -fromfile manytests/typed/003fib.ml - val fib_acc : int -> int -> int -> int - val fib : int -> int - val main : int - - $ ../repl/REPL.exe -fromfile manytests/typed/004manyargs.ml - val wrap : 'a -> 'a - val test3 : int -> int -> int -> int - val test10 : int -> int -> int -> int -> int -> int -> int -> int -> int -> int -> int - val main : int - - $ ../repl/REPL.exe -fromfile manytests/typed/005fix.ml - val fix : (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b - val fac : (int -> int) -> int -> int - val main : int - - $ ../repl/REPL.exe -fromfile manytests/typed/006partial.ml - val foo : int -> int - val main : int - - $ ../repl/REPL.exe -fromfile manytests/typed/006partial2.ml - val foo : int -> int -> int -> int - val main : int - - $ ../repl/REPL.exe -fromfile manytests/typed/006partial3.ml - val foo : int -> int -> int -> unit - val main : int - - $ ../repl/REPL.exe -fromfile manytests/typed/007order.ml - val _start : unit -> unit -> int -> unit -> int -> int -> unit -> int -> int -> int - val main : unit - - $ ../repl/REPL.exe -fromfile manytests/typed/008ascription.ml - val addi : ('a -> bool -> int) -> ('a -> bool) -> 'a -> int - val main : int - - $ ../repl/REPL.exe -fromfile manytests/typed/009let_poly.ml - val temp : int * bool - - $ ../repl/REPL.exe -fromfile manytests/typed/010sukharev.ml - val _1 : int -> int -> int * 'a -> bool - val _2 : int - val _3 : (int * string) option - val _4 : int -> 'a - val _5 : int - val _6 : 'a option -> 'a - val int_of_option : int option -> int - val _42 : int -> bool - val id1 : 'a -> 'a - val id2 : 'b -> 'b - - $ ../repl/REPL.exe -fromfile manytests/typed/015tuples.ml - val fix : (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b - val map : ('b -> 'a) -> 'b * 'b -> 'a * 'a - val fixpoly : (('a -> 'b) * ('a -> 'b) -> 'a -> 'b) * (('a -> 'b) * ('a -> 'b) -> 'a -> 'b) -> ('a -> 'b) * ('a -> 'b) - val feven : 'a * (int -> int) -> int -> int - val fodd : (int -> int) * 'a -> int -> int - val tie : (int -> int) * (int -> int) - val meven : int -> int - val modd : int -> int - val main : int - - $ ../repl/REPL.exe -fromfile manytests/typed/016lists.ml - val length : 'a list -> int - val length_tail : 'a list -> int - val map : ('a -> 'b) -> 'a list -> 'b list - val append : 'a list -> 'a list -> 'a list - val concat : 'a list list -> 'a list - val iter : ('a -> unit) -> 'a list -> unit - val cartesian : 'b list -> 'a list -> ('b * 'a) list - val main : int + $ ../repl/REPL.exe < let a = ;; + > let rec x = x + 1;; + > let f b = b;; + > f "const";; + Parsing error: : end_of_input + Inferencer error: This kind of expression is not allowed as right-hand side of `let rec' + *** Printed *** + + *** Output *** + val f : 'a -> 'a = + *** Printed *** + + *** Output *** + - : string = "const" diff --git a/OCamlPrintf/tests/dune b/OCamlPrintf/tests/dune index bae90a212..60fbc805f 100644 --- a/OCamlPrintf/tests/dune +++ b/OCamlPrintf/tests/dune @@ -1,7 +1,7 @@ (library (name tests) (libraries ocaml_printf_lib) - (modules Test_parser Test_pprinter Test_inferencer) + (modules Test_parser Test_pprinter Test_inferencer Test_interpreter) (preprocess (pps ppx_expect ppx_deriving.show)) (inline_tests) @@ -9,7 +9,7 @@ (backend bisect_ppx))) (cram - (applies_to run_REPL) + (applies_to REPL infer eval) (deps ../repl/REPL.exe factorial.txt @@ -17,7 +17,12 @@ manytests/do_not_type/002if.ml manytests/do_not_type/003occurs.ml manytests/do_not_type/004let_poly.ml + manytests/do_not_type/005.ml manytests/do_not_type/015tuples.ml + manytests/do_not_type/016tuples_mismatch.ml + manytests/do_not_type/097fun_vs_list.ml + manytests/do_not_type/097fun_vs_unit.ml + manytests/do_not_type/098rec_int.ml manytests/do_not_type/099.ml manytests/typed/001fac.ml manytests/typed/002fac.ml @@ -42,5 +47,5 @@ (backend bisect_ppx))) (cram - (applies_to run_qchecker) + (applies_to qchecker) (deps run_qchecker.exe)) diff --git a/OCamlPrintf/tests/eval.t b/OCamlPrintf/tests/eval.t new file mode 100644 index 000000000..3886d5bf9 --- /dev/null +++ b/OCamlPrintf/tests/eval.t @@ -0,0 +1,124 @@ +Copyright 2024-2025, Friend-zva, RodionovMaxim05 +SPDX-License-Identifier: LGPL-3.0-or-later + + $ ../repl/REPL.exe -fromfile manytests/typed/001fac.ml + *** Printed *** + 24 + *** Output *** + val fac : int -> int = + val main : int = 0 + + $ ../repl/REPL.exe -fromfile manytests/typed/002fac.ml + *** Printed *** + 24 + *** Output *** + val fac_cps : int -> (int -> 'a) -> 'a = + val main : int = 0 + + $ ../repl/REPL.exe -fromfile manytests/typed/003fib.ml + *** Printed *** + 33 + *** Output *** + val fib_acc : int -> int -> int -> int = + val fib : int -> int = + val main : int = 0 + + $ ../repl/REPL.exe -fromfile manytests/typed/004manyargs.ml + *** Printed *** + 1111111111110100 + *** Output *** + val wrap : 'a -> 'a = + val test3 : int -> int -> int -> int = + val test10 : int -> int -> int -> int -> int -> int -> int -> int -> int -> int -> int = + val main : int = 0 + + $ ../repl/REPL.exe -fromfile manytests/typed/005fix.ml + *** Printed *** + 720 + *** Output *** + val fix : (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b = + val fac : (int -> int) -> int -> int = + val main : int = 0 + + $ ../repl/REPL.exe -fromfile manytests/typed/006partial.ml + *** Printed *** + 1122 + *** Output *** + val foo : int -> int = + val main : int = 0 + + $ ../repl/REPL.exe -fromfile manytests/typed/006partial2.ml + *** Printed *** + 1237 + *** Output *** + val foo : int -> int -> int -> int = + val main : int = 0 + + $ ../repl/REPL.exe -fromfile manytests/typed/006partial3.ml + *** Printed *** + 489 + *** Output *** + val foo : int -> int -> int -> unit = + val main : int = 0 + + $ ../repl/REPL.exe -fromfile manytests/typed/007order.ml + *** Printed *** + 124-1103-55555510000 + *** Output *** + val _start : unit -> unit -> int -> unit -> int -> int -> unit -> int -> int -> int = + val main : unit = () + + $ ../repl/REPL.exe -fromfile manytests/typed/008ascription.ml + *** Printed *** + 8 + *** Output *** + val addi : ('a -> bool -> int) -> ('a -> bool) -> 'a -> int = + val main : int = 0 + + $ ../repl/REPL.exe -fromfile manytests/typed/009let_poly.ml + *** Printed *** + + *** Output *** + val temp : int * bool = (1, true) + + $ ../repl/REPL.exe -fromfile manytests/typed/010sukharev.ml + *** Printed *** + + *** Output *** + val _1 : int -> int -> int * 'a -> bool = + val _2 : int = 1 + val _3 : (int * string) option = Some (1, "hi") + val _4 : int -> 'a = + val _5 : int = 42 + val _6 : 'a option -> 'a = + val int_of_option : int option -> int = + val _42 : int -> bool = + val id1 : 'a -> 'a = + val id2 : 'b -> 'b = + + $ ../repl/REPL.exe -fromfile manytests/typed/015tuples.ml + *** Printed *** + 1111 + *** Output *** + val fix : (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b = + val map : ('b -> 'a) -> 'b * 'b -> 'a * 'a = + val fixpoly : (('a -> 'b) * ('a -> 'b) -> 'a -> 'b) * (('a -> 'b) * ('a -> 'b) -> 'a -> 'b) -> ('a -> 'b) * ('a -> 'b) = + val feven : 'a * (int -> int) -> int -> int = + val fodd : (int -> int) * 'a -> int -> int = + val tie : (int -> int) * (int -> int) = (, ) + val meven : int -> int = + val modd : int -> int = + val main : int = 0 + + $ ../repl/REPL.exe -fromfile manytests/typed/016lists.ml + *** Printed *** + 1238 + *** Output *** + val length : 'a list -> int = + val length_tail : 'a list -> int = + val map : ('a -> 'b) -> 'a list -> 'b list = + val append : 'a list -> 'a list -> 'a list = + val concat : 'a list list -> 'a list = + val iter : ('a -> unit) -> 'a list -> unit = + val cartesian : 'b list -> 'a list -> ('b * 'a) list = + val main : int = 0 diff --git a/OCamlPrintf/tests/infer.t b/OCamlPrintf/tests/infer.t new file mode 100644 index 000000000..e14117260 --- /dev/null +++ b/OCamlPrintf/tests/infer.t @@ -0,0 +1,118 @@ +Copyright 2024-2025, Friend-zva, RodionovMaxim05 +SPDX-License-Identifier: LGPL-3.0-or-later + + $ ../repl/REPL.exe -inference -fromfile factorial.txt + val factorial : int -> int + + $ ../repl/REPL.exe -inference -fromfile manytests/do_not_type/001.ml + Inferencer error: Undefined variable 'fac' + + $ ../repl/REPL.exe -inference -fromfile manytests/do_not_type/002if.ml + Inferencer error: Unification failed on int and bool + + $ ../repl/REPL.exe -inference -fromfile manytests/do_not_type/003occurs.ml + Inferencer error: Occurs check failed: the type variable 'ty1 occurs inside 'ty1 -> 'ty3 + + $ ../repl/REPL.exe -inference -fromfile manytests/do_not_type/004let_poly.ml + Inferencer error: Unification failed on int and bool + + $ ../repl/REPL.exe -inference -fromfile manytests/do_not_type/005.ml + Inferencer error: Unification failed on string and int + + $ ../repl/REPL.exe -inference -fromfile manytests/do_not_type/015tuples.ml + Inferencer error: Only variables are allowed as left-hand side of `let rec' + + $ ../repl/REPL.exe -inference -fromfile manytests/do_not_type/016tuples_mismatch.ml + Inferencer error: Unification failed on int * int * int and 'ty0 * 'ty1 + + $ ../repl/REPL.exe -inference -fromfile manytests/do_not_type/097fun_vs_list.ml + Inferencer error: Unification failed on 'ty0 -> 'ty0 and 'ty1 list + + $ ../repl/REPL.exe -inference -fromfile manytests/do_not_type/097fun_vs_unit.ml + Inferencer error: Unification failed on 'ty0 -> 'ty0 and unit + + $ ../repl/REPL.exe -inference -fromfile manytests/do_not_type/098rec_int.ml + Inferencer error: This kind of expression is not allowed as right-hand side of `let rec' + + $ ../repl/REPL.exe -inference -fromfile manytests/do_not_type/099.ml + Inferencer error: Only variables are allowed as left-hand side of `let rec' + + $ ../repl/REPL.exe -inference -fromfile manytests/typed/001fac.ml + val fac : int -> int + val main : int + + $ ../repl/REPL.exe -inference -fromfile manytests/typed/002fac.ml + val fac_cps : int -> (int -> 'a) -> 'a + val main : int + + $ ../repl/REPL.exe -inference -fromfile manytests/typed/003fib.ml + val fib_acc : int -> int -> int -> int + val fib : int -> int + val main : int + + $ ../repl/REPL.exe -inference -fromfile manytests/typed/004manyargs.ml + val wrap : 'a -> 'a + val test3 : int -> int -> int -> int + val test10 : int -> int -> int -> int -> int -> int -> int -> int -> int -> int -> int + val main : int + + $ ../repl/REPL.exe -inference -fromfile manytests/typed/005fix.ml + val fix : (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b + val fac : (int -> int) -> int -> int + val main : int + + $ ../repl/REPL.exe -inference -fromfile manytests/typed/006partial.ml + val foo : int -> int + val main : int + + $ ../repl/REPL.exe -inference -fromfile manytests/typed/006partial2.ml + val foo : int -> int -> int -> int + val main : int + + $ ../repl/REPL.exe -inference -fromfile manytests/typed/006partial3.ml + val foo : int -> int -> int -> unit + val main : int + + $ ../repl/REPL.exe -inference -fromfile manytests/typed/007order.ml + val _start : unit -> unit -> int -> unit -> int -> int -> unit -> int -> int -> int + val main : unit + + $ ../repl/REPL.exe -inference -fromfile manytests/typed/008ascription.ml + val addi : ('a -> bool -> int) -> ('a -> bool) -> 'a -> int + val main : int + + $ ../repl/REPL.exe -inference -fromfile manytests/typed/009let_poly.ml + val temp : int * bool + + $ ../repl/REPL.exe -inference -fromfile manytests/typed/010sukharev.ml + val _1 : int -> int -> int * 'a -> bool + val _2 : int + val _3 : (int * string) option + val _4 : int -> 'a + val _5 : int + val _6 : 'a option -> 'a + val int_of_option : int option -> int + val _42 : int -> bool + val id1 : 'a -> 'a + val id2 : 'b -> 'b + + $ ../repl/REPL.exe -inference -fromfile manytests/typed/015tuples.ml + val fix : (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b + val map : ('b -> 'a) -> 'b * 'b -> 'a * 'a + val fixpoly : (('a -> 'b) * ('a -> 'b) -> 'a -> 'b) * (('a -> 'b) * ('a -> 'b) -> 'a -> 'b) -> ('a -> 'b) * ('a -> 'b) + val feven : 'a * (int -> int) -> int -> int + val fodd : (int -> int) * 'a -> int -> int + val tie : (int -> int) * (int -> int) + val meven : int -> int + val modd : int -> int + val main : int + + $ ../repl/REPL.exe -inference -fromfile manytests/typed/016lists.ml + val length : 'a list -> int + val length_tail : 'a list -> int + val map : ('a -> 'b) -> 'a list -> 'b list + val append : 'a list -> 'a list -> 'a list + val concat : 'a list list -> 'a list + val iter : ('a -> unit) -> 'a list -> unit + val cartesian : 'b list -> 'a list -> ('b * 'a) list + val main : int diff --git a/OCamlPrintf/tests/run_qchecker.t b/OCamlPrintf/tests/qchecker.t similarity index 100% rename from OCamlPrintf/tests/run_qchecker.t rename to OCamlPrintf/tests/qchecker.t diff --git a/OCamlPrintf/tests/test_inferencer.ml b/OCamlPrintf/tests/test_inferencer.ml index a4e70615e..2daacb34f 100644 --- a/OCamlPrintf/tests/test_inferencer.ml +++ b/OCamlPrintf/tests/test_inferencer.ml @@ -9,14 +9,14 @@ open Ocaml_printf_lib.Inferencer let run str = match parse str with | Ok ast -> - (match run_inferencer ast empty_env with - | Ok out_list -> + (match run_inferencer empty_env ast with + | Ok (_, out_list) -> List.iter (function | Some id, type' -> Format.printf "val %s : %a\n" id pp_core_type type' | None, type' -> Format.printf "- : %a\n" pp_core_type type') out_list - | Error e -> Format.printf "Infer error: %a\n" pp_error e) + | Error e -> Format.printf "Inferencer error: %a\n" pp_error e) | Error _ -> Format.printf "Parsing error\n" ;; @@ -34,7 +34,7 @@ let%expect_test "type check undefined variable" = let a = b |}; [%expect {| - Infer error: Undefined variable 'b' + Inferencer error: Undefined variable 'b' |}] ;; @@ -135,7 +135,7 @@ let%expect_test "type check error in recursive let expression" = |}; [%expect {| - Infer error: This kind of expression is not allowed as right-hand side of `let rec' + Inferencer error: This kind of expression is not allowed as right-hand side of `let rec' |}] ;; @@ -183,8 +183,9 @@ let%expect_test "type check pattern bound the variable multiple times" = | x, x -> true | _ -> false |}; - [%expect {| - Infer error: Variable 'x' is bound several times in the matching + [%expect + {| + Inferencer error: Variable 'x' is bound several times in the matching |}] ;; @@ -202,7 +203,7 @@ let%expect_test "type check invalid expression list" = let f a = [true; a; 2] |}; [%expect {| - Infer error: Unification failed on bool and int + Inferencer error: Unification failed on bool and int |}] ;; diff --git a/OCamlPrintf/tests/test_interpreter.ml b/OCamlPrintf/tests/test_interpreter.ml new file mode 100644 index 000000000..5385fea7b --- /dev/null +++ b/OCamlPrintf/tests/test_interpreter.ml @@ -0,0 +1,126 @@ +(** Copyright 2024-2025, Friend-zva, RodionovMaxim05 *) + +(** SPDX-License-Identifier: LGPL-3.0-or-later *) + +open Ocaml_printf_lib.Parser +open Ocaml_printf_lib.Interpreter + +let run str = + match parse str with + | Ok ast -> + (match run_interpreter empty_env ast with + | Ok (_, out_list) -> + List.iter + (function + | Some id, val' -> Format.printf "val %s = %a\n" id pp_value val' + | None, val' -> Format.printf "- = %a\n" pp_value val') + out_list + | Error e -> Format.printf "Interpreter error: %a\n" pp_error e) + | Error _ -> Format.printf "Parsing error\n" +;; + +let%expect_test "parsing error" = + run {| + let a = ;; + |}; + [%expect {| + Parsing error + |}] +;; + +let%expect_test "eval simple let binding" = + run {| + let a = -(4 + 4) + and b = true;; + |}; + [%expect {| + val a = -8 + val b = true + |}] +;; + +let%expect_test "eval tuple and list let bindings" = + run {| + let a, b = 1, (2, 3);; + let [ c; d ] = 3 :: 4 :: [] + |}; + [%expect {| + val a = 1 + val b = (2, 3) + val c = 3 + val d = 4 + |}] +;; + +let%expect_test "eval `let in'" = + run {| + let f = + let x = "abc" in + let y = "qwerty" in + x <> y + ;; + |}; + [%expect {| + val f = true + |}] +;; + +let%expect_test "eval 'Struct_eval'" = + run {| + 1;; + |}; + [%expect {| + - = 1 + |}] +;; + +let%expect_test "eval 'Exp_fun'" = + run {| + let foo x y = x * y + let q = foo 1 6 + let w = foo 2 (-5) + |}; + [%expect {| + val foo = + val q = 6 + val w = -10 + |}] +;; + +let%expect_test "eval recursive value binding 1" = + run {| + let rec x = 21 and y = x + 1;; + |}; + [%expect {| + val x = 21 + val y = 22 + |}] +;; + +let%expect_test "eval recursive value binding 2" = + run + {| + let rec factorial n = if n <= 1 then 1 else n * factorial (n - 1);; + factorial 5 + |}; + [%expect {| + val factorial = + - = 120 + |}] +;; + +let%expect_test "eval pattern-matching" = + run + {| + let f = + match [ 1; 2; 3 ] with + | a :: [] -> a + | a :: b :: [] -> a + b + | a :: b :: c :: [] -> a + b + c + | _ -> 0 + ;; + |}; + [%expect {| + val f = 6 + |}] +;; diff --git a/OCamlPrintf/tests/test_interpreter.mli b/OCamlPrintf/tests/test_interpreter.mli new file mode 100644 index 000000000..7d4ee4852 --- /dev/null +++ b/OCamlPrintf/tests/test_interpreter.mli @@ -0,0 +1,3 @@ +(** Copyright 2024-2025, Friend-zva, RodionovMaxim05 *) + +(** SPDX-License-Identifier: LGPL-3.0-or-later *)