문법은 건드리지 않았다. 표본이 한 사람이 쓴 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
97 lines
2.9 KiB
Plaintext
97 lines
2.9 KiB
Plaintext
// 설정 파일 리포트 도구.
|
|
//
|
|
// 파일을 읽고, 파싱하고, 문제를 보고하고, 요약을 낸다.
|
|
// 권한은 셋뿐이다 — 읽기(File), 인자(Args), 출력(Console). 그 밖의 일은
|
|
// 이 프로그램이 할 수 없다. 시그니처에 적힌 것이 이 프로그램의 전부다.
|
|
|
|
import "config" as Cfg
|
|
import "cool.dev/std/list" as List
|
|
import "cool.dev/std/string" as String
|
|
import "cool.dev/std/int" as Int
|
|
import "cool.dev/std/option" as Option
|
|
|
|
pub capability Console {
|
|
fn print(s: String) effects {Console.print}
|
|
}
|
|
|
|
pub capability File {
|
|
fn read(path: String) effects {File.read} -> Result[String, String]
|
|
}
|
|
|
|
pub capability Args {
|
|
fn all() effects {Args.all} -> List[String]
|
|
}
|
|
|
|
pub fn report(c: Console, cfg: Cfg.Config) effects {Console.print} {
|
|
c.print("설정")
|
|
List.each(cfg.entries, fn(e) {
|
|
c.print(String.join("", [
|
|
" ", e.key, " = ", Cfg.show_value(e.value),
|
|
" (", Cfg.type_name(e.value), ")",
|
|
]))
|
|
})
|
|
|
|
c.print("")
|
|
c.print(String.join("", [
|
|
"항목 ", Int.show(List.len(cfg.entries)),
|
|
"개, 문제 ", Int.show(List.len(cfg.problems)), "개",
|
|
]))
|
|
|
|
if List.is_empty(cfg.problems) {
|
|
c.print("문제 없음")
|
|
} else {
|
|
c.print("")
|
|
c.print("문제")
|
|
List.each(cfg.problems, fn(p) {
|
|
c.print(String.join("", [ " ", Int.show(p.line), "행: ", p.message ]))
|
|
})
|
|
}
|
|
}
|
|
|
|
// 타입이 있는 조회. 없거나 타입이 다르면 Err이고, 둘 다 사람이 읽을 수 있다.
|
|
pub fn check_known(c: Console, cfg: Cfg.Config) effects {Console.print} {
|
|
c.print("")
|
|
c.print("알려진 설정")
|
|
show_int(c, cfg, "threads")
|
|
show_int(c, cfg, "port")
|
|
show_flag(c, cfg, "verbose")
|
|
}
|
|
|
|
pub fn show_int(c: Console, cfg: Cfg.Config, key: String) effects {Console.print} {
|
|
match Cfg.get_int(cfg, key) {
|
|
Ok(n) => c.print(String.join("", [ " ", key, " = ", Int.show(n) ])),
|
|
Err(m) => c.print(String.concat(" ", m)),
|
|
}
|
|
}
|
|
|
|
pub fn show_flag(c: Console, cfg: Cfg.Config, key: String) effects {Console.print} {
|
|
match Cfg.get_flag(cfg, key) {
|
|
Ok(b) => c.print(String.join("", [
|
|
" ", key, " = ", if b { "true" } else { "false" },
|
|
])),
|
|
Err(m) => c.print(String.concat(" ", m)),
|
|
}
|
|
}
|
|
|
|
pub fn first_arg(xs: List[String]) -> Result[String, String] {
|
|
Option.ok_or(List.first(xs), "설정 파일 경로가 필요합니다")
|
|
}
|
|
|
|
pub fn main(c: Console, f: File, a: Args)
|
|
effects {Console.print, File.read, Args.all} {
|
|
match run(c, f, a) {
|
|
Ok(_) => unit,
|
|
Err(m) => c.print(String.concat("오류: ", m)),
|
|
}
|
|
}
|
|
|
|
pub fn run(c: Console, f: File, a: Args)
|
|
effects {Console.print, File.read, Args.all} -> Result[Unit, String] {
|
|
let path = first_arg(a.all())?
|
|
let text = f.read(path)?
|
|
let cfg = Cfg.parse(text)
|
|
report(c, cfg)
|
|
check_known(c, cfg)
|
|
Ok(unit)
|
|
}
|