Files
coollang/lib/lexical_doc.ml
T
coolguyandClaude Opus 5 78ef07d2ee panic: panic/Never와 내장 테스트 — 문법을 먼저 고치고 대조 장치가 파서를 지적했다
순서가 요점이다. 문법에 test_decl과 panic_expr을 넣고 파서는 안 고친 채로
대조 장치를 돌렸더니 즉시 잡혔다:

  문장 300개 중 파서가 거부한 것 140개
  [1] 선언 (fn, struct, enum, capability, const)이(가) 필요합니다 — test 발견

파서를 따라가게 하니 다시 0건. 문법과 구현이 어긋나는 상태가 관측 가능한
것이 되었다는 뜻이다.

panic:
- 키워드다. prelude가 없어 함수로 두면 쓸 때마다 import해야 한다
- effect가 아니다. 경계 검사 하나에 {Panic}이 호출자 전부로 전염되면
  effect 절은 신호가 아니라 잡음이 된다
- Never는 어떤 타입 자리에도 놓인다. 없으면 panic을 match 팔에서 못 쓴다
- 언어 수준 recover 없음. 되감기 없음. 자원 해제 여부는 열어둔다
- 0으로 나누기, assert 실패가 이 하나로 모인다

test:
- 파라미터가 없어 capability를 받을 수 없고, 만들 문법도 없다. 그래서
  effect-free임이 증명된다 — 관례가 아니라 검사다. 시험해 보니 실제로
  "테스트는 effect를 수행할 수 없습니다"로 거부한다
- 일반 코드와 같은 타입/effect/move 검사를 받는다
- interface hash에서 제외 — 테스트를 고쳤다고 downstream이 재검사되면 안 된다
- 격리는 런타임의 일이다. 하나가 죽어도 나머지는 돈다

assert는 std/test.cool에 coollang으로 쓰였다 — panic 위의 설탕임이 코드로
보이고, std에서 본문이 있는 첫 함수가 됐다. 그 바람에 std/런타임 양방향
테스트가 걸렸고(본문 있는 함수에 런타임 구현을 요구했다), 그 구분을 넣었다.

samples/app/config.cool에 첫 테스트 넷.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 17:51:56 +09:00

61 lines
2.2 KiB
OCaml

(* 문법 문서의 어휘 절을 코드에서 생성한다.
키워드 표와 "줄을 끝낼 수 있는 토큰" 목록은 지금까지 token.ml과
grammar.ebnf 양쪽에 손으로 적혀 있었다. 그런 목록은 어긋난다 —
실제로 이 프로젝트의 문법 문서 전체가 그렇게 어긋났다.
여기서 생성하고 테스트가 대조하므로, 이제 토큰을 추가하면 문서가 낡거나
빌드가 깨진다. 사람이 두 곳을 맞출 의무가 없다. *)
let display (k : Token.kind) =
match k with
| Token.Ident _ -> Some "ident"
| Token.Int _ -> Some "int_lit"
| Token.Str _ -> Some "string_lit"
| Token.Newline | Token.Eof -> None
| _ -> Some ("\"" ^ Token.show_kind k ^ "\"")
let is_keyword (k : Token.kind) = Token.keyword (Token.show_kind k) = Some k
let keywords () =
List.filter_map
(fun k ->
if is_keyword k then Some ("\"" ^ Token.show_kind k ^ "\"") else None)
Token.all_kinds
let statement_enders () =
List.filter_map
(fun k -> if Token.can_end_statement k then display k else None)
Token.all_kinds
let begin_mark = "(* 여기부터 lib/token.ml에서 생성됩니다 — 손으로 고치지 마십시오 *)"
let end_mark = "(* 생성 끝 *)"
(* 한 줄이 길어지지 않게 접는다. 구분자는 줄 끝에 남겨 이어짐이 보이게 한다 *)
let wrap ~indent items =
let rec go acc line = function
| [] -> List.rev (line :: acc)
| x :: rest ->
if line = indent then go acc (line ^ x) rest
else if String.length line + String.length x + 3 > 72 then
go ((line ^ " |") :: acc) (indent ^ x) rest
else go acc (line ^ " | " ^ x) rest
in
String.concat "\n" (go [] indent items)
let render () =
String.concat "\n"
[
begin_mark;
" * 줄을 끝낼 수 있는 토큰. 줄의 마지막 토큰이 이 중 하나이면 그 줄 끝에";
" * NEWLINE이 삽입된다. Token.can_end_statement가 원본이다.";
" *";
wrap ~indent:" * " (statement_enders ());
" *";
" * 키워드. 이름으로 쓸 수 없다. Token.keyword가 원본이다.";
" *";
wrap ~indent:" * " (keywords ());
" " ^ end_mark;
]