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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user