recognize: 문법이 직접 읽는 인식기 — 파서와 판정을 대조한다

lib/recognize.ml은 docs/grammar.ebnf를 그대로 해석해 토큰 열을 받아들일지
판정한다. AST를 만들지 않는다 — 판정만 하므로 손 파서의 진단은 그대로
남는다.

문법이 LL(1)임을 이미 검증했으므로 선택이 결정적이다. 다음 토큰이 어느
대안의 FIRST에 있는지만 보고 되돌아가지 않는다.

같은 토큰 열을 손 파서와 인식기 양쪽에 주고 판정이 갈리는지 테스트가
검사한다. 저장소의 .cool 25개 전부에서 일치한다. 갈리면 빌드가 깨진다.

이제 "설명서를 잘 관리하자"에 기대지 않는다. 그 방법은 이미 실패했다 —
설명서가 어긋났고 아무도 몰랐고 나조차 안 읽었다.

확인: 문법에서 한정 이름(named_type의 "." ident)을 빼 보면 즉시
"파서는 받고 문법은 거부"가 두 파일에서 잡힌다.

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 17:06:02 +09:00
co-authored by Claude Opus 5
parent bde940eda3
commit 61b1920909
3 changed files with 209 additions and 0 deletions
+51
View File
@@ -1375,3 +1375,54 @@ let () =
print_endline " 현재 코드가 만드는 블록:";
print_endline block
end
(* ------------------------------------------------------------------ *)
(* 문법과 파서의 대조 *)
(* *)
(* 문법 파일에서 직접 읽는 인식기와 손으로 쓴 파서에 같은 토큰 열을 주고 *)
(* 판정이 같은지 본다. 갈리면 둘 중 하나가 틀린 것이고 빌드가 깨진다. *)
(* 이것이 설명서와 구현이 어긋나지 않게 하는 유일한 장치다 — *)
(* "문서를 잘 관리하자"는 이미 실패했다. *)
(* ------------------------------------------------------------------ *)
let () =
let read f =
let ic = open_in_bin f in
let n = in_channel_length ic in
let s = really_input_string ic n in
close_in ic;
s
in
let g =
match Ebnf.parse_result (read "../docs/grammar.ebnf") with
| Ok g -> g
| Error e -> failwith (Printf.sprintf "문법 %d행: %s" e.line e.msg)
in
let tokens =
[ "ident"; "int_lit"; "string_lit"; "NEWLINE"; "char"; "digit"; "letter" ]
in
let files =
List.concat_map
(fun dir ->
try
Sys.readdir dir |> Array.to_list
|> List.filter (fun f -> Filename.check_suffix f ".cool")
|> List.sort compare
|> List.map (Filename.concat dir)
with Sys_error _ -> [])
[ "../samples"; "../samples/modules"; "../samples/run"; "../samples/app"; "../std" ]
in
check "대조할 파일이 있다" (List.length files > 15);
List.iter
(fun f ->
match Lexer.lex_result (read f) with
| Error _ -> ()
| Ok toks ->
let hand = Result.is_ok (Parser.parse_result toks) in
let spec = Result.is_ok (Recognize.check ~tokens g toks) in
if hand <> spec then
Printf.printf " (%s: 파서 %s / 문법 %s)\n" f
(if hand then "받음" else "거부")
(if spec then "받음" else "거부");
check (Filename.basename f ^ ": 문법과 파서의 판정이 같다") (hand = spec))
files