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
+45
View File
@@ -6,7 +6,52 @@ let read file =
close_in ic;
s
(* 문법과 손 파서를 같은 파일에 돌려 판정이 갈리는지 본다 *)
let compare_files files =
let g =
match Coollang.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 bad = ref 0 in
List.iter
(fun f ->
match Coollang.Lexer.lex_result (read f) with
| Error e -> Printf.printf " 렉서 오류 %s:%d:%d %s\n" f e.pos.line e.pos.col e.msg
| Ok toks ->
let hand =
match Coollang.Parser.parse_result toks with
| Ok _ -> None
| Error e -> Some (e.pos, e.msg)
in
let spec =
match Coollang.Recognize.check ~tokens g toks with
| Ok () -> None
| Error e ->
Some (e.pos, Printf.sprintf "%s이(가) 필요한데 %s" e.expected e.got)
in
(match (hand, spec) with
| None, None -> ()
| Some _, Some _ -> ()
| None, Some (p, m) ->
incr bad;
Printf.printf "갈림 %s: 파서는 받고 문법은 거부 (%d:%d %s)\n" f
p.Coollang.Token.line p.Coollang.Token.col m
| Some (p, m), None ->
incr bad;
Printf.printf "갈림 %s: 문법은 받고 파서는 거부 (%d:%d %s)\n" f
p.Coollang.Token.line p.Coollang.Token.col m))
files;
Printf.printf "\n파일 %d개 중 판정이 갈린 것 %d개\n" (List.length files) !bad;
if !bad > 0 then exit 1
let () =
if Array.length Sys.argv > 2 && Sys.argv.(1) = "--compare" then (
compare_files (Array.to_list (Array.sub Sys.argv 2 (Array.length Sys.argv - 2)));
exit 0);
if Array.length Sys.argv > 1 && Sys.argv.(1) = "--lexical" then (
print_string (Coollang.Lexical_doc.render ());
print_newline ();