@@ -3279,6 +3279,83 @@ let stdlib_04a_mut_tests = [
32793279 Alcotest. test_case " #328 Deno codegen emits __cell shape" `Quick test_stdlib_04a_mut_deno_codegen;
32803280]
32813281
3282+ (* ---- STDLIB-04e: Pure externs (Refs #332) ----
3283+
3284+ Three externs declared in stdlib/effects.affine as pure:
3285+ int_to_string : Int -> String
3286+ string_to_int : String -> Option<Int>
3287+ string_length : String -> Int
3288+
3289+ `int_to_string` + `string_length` were already wired; `string_to_int`
3290+ was unwired (dead surface — any caller would compile and fail at run).
3291+ This row wires `string_to_int` as the typed-alias of `parse_int` and
3292+ asserts hermetic round-trip semantics for all three. *)
3293+
3294+ let test_stdlib_04e_int_to_string () =
3295+ let prog = Parse_driver. parse_string ~file: " <test>"
3296+ " fn f() -> String { int_to_string(42) }" in
3297+ match Interp. eval_program prog with
3298+ | Error e -> Alcotest. failf " interp failed: %s" (Value. show_eval_error e)
3299+ | Ok env ->
3300+ (match Value. lookup_env " f" env with
3301+ | Ok fn ->
3302+ (match Interp. apply_function fn [] with
3303+ | Ok (Value. VString "42" ) -> ()
3304+ | Ok v -> Alcotest. failf " expected VString \" 42\" , got %s"
3305+ (Value. show_value v)
3306+ | Error e -> Alcotest. failf " apply failed: %s" (Value. show_eval_error e))
3307+ | Error e -> Alcotest. failf " lookup f failed: %s" (Value. show_eval_error e))
3308+
3309+ let test_stdlib_04e_string_to_int_some () =
3310+ let prog = Parse_driver. parse_string ~file: " <test>"
3311+ " fn f() -> Option<Int> { string_to_int(\" 123\" ) }" in
3312+ match Interp. eval_program prog with
3313+ | Error e -> Alcotest. failf " interp failed: %s" (Value. show_eval_error e)
3314+ | Ok env ->
3315+ (match Value. lookup_env " f" env with
3316+ | Ok fn ->
3317+ (match Interp. apply_function fn [] with
3318+ | Ok (Value. VVariant ("Some" , Some (Value. VInt 123 ))) -> ()
3319+ | Ok v -> Alcotest. failf " expected Some(123), got %s"
3320+ (Value. show_value v)
3321+ | Error e -> Alcotest. failf " apply failed: %s" (Value. show_eval_error e))
3322+ | Error e -> Alcotest. failf " lookup f failed: %s" (Value. show_eval_error e))
3323+
3324+ let test_stdlib_04e_string_to_int_none () =
3325+ let prog = Parse_driver. parse_string ~file: " <test>"
3326+ " fn f() -> Option<Int> { string_to_int(\" abc\" ) }" in
3327+ match Interp. eval_program prog with
3328+ | Error e -> Alcotest. failf " interp failed: %s" (Value. show_eval_error e)
3329+ | Ok env ->
3330+ (match Value. lookup_env " f" env with
3331+ | Ok fn ->
3332+ (match Interp. apply_function fn [] with
3333+ | Ok (Value. VVariant ("None" , None)) -> ()
3334+ | Ok v -> Alcotest. failf " expected None, got %s" (Value. show_value v)
3335+ | Error e -> Alcotest. failf " apply failed: %s" (Value. show_eval_error e))
3336+ | Error e -> Alcotest. failf " lookup f failed: %s" (Value. show_eval_error e))
3337+
3338+ let test_stdlib_04e_string_length () =
3339+ let prog = Parse_driver. parse_string ~file: " <test>"
3340+ " fn f() -> Int { string_length(\" hello\" ) }" in
3341+ match Interp. eval_program prog with
3342+ | Error e -> Alcotest. failf " interp failed: %s" (Value. show_eval_error e)
3343+ | Ok env ->
3344+ (match Value. lookup_env " f" env with
3345+ | Ok fn ->
3346+ (match Interp. apply_function fn [] with
3347+ | Ok (Value. VInt 5 ) -> ()
3348+ | Ok v -> Alcotest. failf " expected VInt 5, got %s" (Value. show_value v)
3349+ | Error e -> Alcotest. failf " apply failed: %s" (Value. show_eval_error e))
3350+ | Error e -> Alcotest. failf " lookup f failed: %s" (Value. show_eval_error e))
3351+
3352+ let stdlib_04e_pure_tests = [
3353+ Alcotest. test_case " #332 int_to_string(42) == \" 42\" " `Quick test_stdlib_04e_int_to_string;
3354+ Alcotest. test_case " #332 string_to_int(\" 123\" ) == Some(123)" `Quick test_stdlib_04e_string_to_int_some;
3355+ Alcotest. test_case " #332 string_to_int(\" abc\" ) == None" `Quick test_stdlib_04e_string_to_int_none;
3356+ Alcotest. test_case " #332 string_length(\" hello\" ) == 5" `Quick test_stdlib_04e_string_length;
3357+ ]
3358+
32823359(* ---- Issue #35 Phase 2 — Vscode bindings ----
32833360
32843361 Verifies stdlib/Vscode.affine and stdlib/VscodeLanguageClient.affine
@@ -3970,6 +4047,7 @@ let tests =
39704047 (" E2E Xmod Other Codegens" , cross_module_other_codegens_tests);
39714048 (" E2E Externs" , extern_tests);
39724049 (" E2E STDLIB-04a Mut #328" , stdlib_04a_mut_tests);
4050+ (" E2E STDLIB-04e Pure #332" , stdlib_04e_pure_tests);
39734051 (" E2E Vscode Bindings" , vscode_bindings_tests);
39744052 (" E2E Array Type Sugar" , array_type_tests);
39754053 (" E2E Qualified Paths #228" , qualified_path_tests);
0 commit comments