std: 마찰 보고의 F1/F3/F6/F7 처리 — 292줄이 244줄로

문법은 건드리지 않았다. 표본이 한 사람이 쓴 300줄 하나뿐인데 되돌리기
비싼 축을 움직일 수는 없다. std 보강만 했다.

추가: List.first/nth, String.join, std/option.cool, std/result.cool.

samples/app 재작성 결과 config.cool 196 → 148줄. head_or, second_or,
Pick, take_at, first_text, first_entry가 통째로 사라졌다. 예측 40줄,
실제 48줄 — F1의 값이 확인됐다.

F3은 줄 수로 값이 안 보인다. main.cool은 96줄 그대로다. 4단 중첩
String.concat이 4줄짜리 String.join 배열이 됐으니 줄 수가 같다. 읽기는
확실히 나아졌다. 줄 수는 읽기 좋음의 대리 지표일 뿐이고 여기서 그 대리가
깨진다 — 다음 개밥 먹기는 다른 것을 재야 한다.

F7: Interp.implemented와 std/*.cool 선언이 서로를 덮는지 테스트가 양방향
으로 검사한다. 어긋나면 빌드가 깨진다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
This commit is contained in:
2026-08-30 16:20:23 +09:00
co-authored by Claude Opus 5
parent 7be05be77f
commit a9f6527825
11 changed files with 260 additions and 90 deletions
+86 -7
View File
@@ -144,10 +144,8 @@ let root_capability name : value option =
match args with
| [ VStr path ] -> (
try VEnum ("Result", "Ok", [ VStr (read_whole path) ])
with Sys_error m ->
VEnum ("Result", "Err", [ VStr m ]))
| _ -> VEnum ("Result", "Err", [ VStr "read: 경로가 필요합니다" ])
);
with Sys_error m -> VEnum ("Result", "Err", [ VStr m ]))
| _ -> VEnum ("Result", "Err", [ VStr "read: 경로가 필요합니다" ]) );
] ))
| "Args" ->
Some
@@ -160,15 +158,56 @@ let root_capability name : value option =
Some (VScope "root")
| _ -> None
(* 런타임이 구현한 이름 전부. std/*.cool의 선언과 이 목록이 어긋나면 검사는
통과하고 실행이 죽는다 — 그 간극을 테스트가 막는다 (docs/friction.md F7).
v1에서 std를 coollang으로 구현하면 이 목록 자체가 사라진다. *)
let implemented =
[
"string.len";
"string.is_empty";
"string.concat";
"string.split";
"string.join";
"string.trim";
"string.starts_with";
"string.contains";
"int.show";
"int.abs";
"int.parse";
"bool.show";
"list.len";
"list.is_empty";
"list.first";
"list.nth";
"list.push";
"list.concat";
"list.reverse";
"list.each";
"list.map";
"list.filter";
"list.fold";
"option.is_some";
"option.map";
"option.unwrap_or";
"option.ok_or";
"result.is_ok";
"result.map";
"result.map_err";
"result.unwrap_or";
]
let builtin pos name (args : value list) : value =
match (name, args) with
| "string.concat", [ VStr a; VStr b ] -> VStr (a ^ b)
| "string.len", [ VStr a ] -> VInt (String.length a)
| "string.is_empty", [ VStr a ] -> VBool (a = "")
| "string.split", [ VStr s; VStr sep ] -> VList (List.map (fun x -> VStr x) (split_on s sep))
| "string.split", [ VStr s; VStr sep ] ->
VList (List.map (fun x -> VStr x) (split_on s sep))
| "string.trim", [ VStr s ] -> VStr (String.trim s)
| "string.starts_with", [ VStr s; VStr p ] ->
VBool (String.length s >= String.length p && String.sub s 0 (String.length p) = p)
VBool
(String.length s >= String.length p
&& String.sub s 0 (String.length p) = p)
| "string.contains", [ VStr s; VStr n ] -> VBool (find_sub s n <> None)
| "int.show", [ VInt n ] -> VStr (string_of_int n)
| "int.abs", [ VInt n ] -> VInt (abs n)
@@ -182,6 +221,27 @@ let builtin pos name (args : value list) : value =
| "list.push", [ VList xs; x ] -> VList (xs @ [ x ])
| "list.concat", [ VList xs; VList ys ] -> VList (xs @ ys)
| "list.reverse", [ VList xs ] -> VList (List.rev xs)
| "list.first", [ VList xs ] -> (
match xs with
| [] -> VEnum ("Option", "None", [])
| x :: _ -> VEnum ("Option", "Some", [ x ]))
| "list.nth", [ VList xs; VInt i ] -> (
match List.nth_opt xs i with
| Some x -> VEnum ("Option", "Some", [ x ])
| None -> VEnum ("Option", "None", []))
| "string.join", [ VStr sep; VList parts ] ->
VStr
(String.concat sep
(List.map (function VStr s -> s | v -> show v) parts))
| "option.is_some", [ VEnum ("Option", v, _) ] -> VBool (v = "Some")
| "option.unwrap_or", [ VEnum ("Option", "Some", [ x ]); _ ] -> x
| "option.unwrap_or", [ _; fallback ] -> fallback
| "option.ok_or", [ VEnum ("Option", "Some", [ x ]); _ ] ->
VEnum ("Result", "Ok", [ x ])
| "option.ok_or", [ _; e ] -> VEnum ("Result", "Err", [ e ])
| "result.is_ok", [ VEnum ("Result", v, _) ] -> VBool (v = "Ok")
| "result.unwrap_or", [ VEnum ("Result", "Ok", [ x ]); _ ] -> x
| "result.unwrap_or", [ _; fallback ] -> fallback
| _ ->
fail pos (Printf.sprintf "%s은(는) 런타임이 제공하지 않습니다 (표준 라이브러리가 아직 없습니다)" name)
@@ -305,9 +365,28 @@ and apply st pos f args =
| [ VList xs; f ] ->
VList
(List.filter
(fun x -> match apply st pos f [ x ] with VBool b -> b | _ -> false)
(fun x ->
match apply st pos f [ x ] with VBool b -> b | _ -> false)
xs)
| _ -> fail pos "list.filter는 리스트와 함수를 받습니다")
| VBuiltin "option.map" -> (
match args with
| [ VEnum ("Option", "Some", [ x ]); f ] ->
VEnum ("Option", "Some", [ apply st pos f [ x ] ])
| [ o; _ ] -> o
| _ -> fail pos "option.map은 Option과 함수를 받습니다")
| VBuiltin "result.map" -> (
match args with
| [ VEnum ("Result", "Ok", [ x ]); f ] ->
VEnum ("Result", "Ok", [ apply st pos f [ x ] ])
| [ r; _ ] -> r
| _ -> fail pos "result.map은 Result와 함수를 받습니다")
| VBuiltin "result.map_err" -> (
match args with
| [ VEnum ("Result", "Err", [ e ]); f ] ->
VEnum ("Result", "Err", [ apply st pos f [ e ] ])
| [ r; _ ] -> r
| _ -> fail pos "result.map_err는 Result와 함수를 받습니다")
| VBuiltin "list.fold" -> (
match args with
| [ VList xs; init; f ] ->