parser: 재귀 하강 구문 분석기와 AST
grammar.ebnf의 프로덕션 하나에 함수 하나로 대응한다. LL(1)이므로 선읽기는
항상 한 토큰이고 backtracking이 없다. 모든 노드가 위치를 들고 다닌다.
문법과 얽히는 두 자리를 원칙대로 처리했다:
- NEWLINE 흡수는 문법에 { NEWLINE }으로 적힌 자리에서만 한다. 파서가
"여기선 줄바꿈 무시" 식으로 임의 판단하면 어디서 무시되는지 아무도 모르게
된다.
- struct 리터럴은 if/match/scope 머리에서 금지하고(no_struct), 괄호·인자
목록·블록에 들어가면 다시 허용한다.
문법에 새긴 제한이 실제로 파서에서 죽는 것을 확인했다. 파라미터 위치의
effect 합집합과 match 가드는 검사기가 아니라 파서가 거부하며, 진단이
원인을 직접 말한다. 후행 콤마 누락도 일반적인 "닫는 괄호 필요" 대신
"다중 줄 목록에는 후행 콤마가 필요합니다"로 보고한다.
샘플을 실제로 파싱해 두 가지를 잡았다:
- 02와 05가 own을 타입 위치에 쓰고 있었다. 확정한 규칙은 바인딩 수식어가
이름 앞이므로 샘플이 틀렸다. 수정.
- 05에 구문 오류와 검사기 오류가 섞여 있었다. 파서가 첫 오류에서 멈추면
검사기 케이스에 영영 도달하지 못하므로 08_syntax_errors.cool로 분리.
grammar.ebnf를 구현과 맞췄다: 제네릭 인자에 effect 집합 허용, 마지막 문의
구분자는 "}" 앞에서 생략, 중괄호 목록 안의 NEWLINE 흡수 위치 명시.
cool ast 추가. cool check는 이제 파서까지 돌리되 여전히 통과했다고 말하지
않는다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
This commit is contained in:
+16
-4
@@ -2,7 +2,7 @@
|
||||
parse -> name resolution -> type check -> effect/capability check
|
||||
-> interface artifact + hash -> (cool run 시) 얇은 typed IR -> interpreter
|
||||
|
||||
현재 구현된 단계: 어휘 분석. *)
|
||||
현재 구현된 단계: 어휘 분석, 구문 분석. *)
|
||||
|
||||
type error = { file : string; line : int; col : int; message : string }
|
||||
|
||||
@@ -30,6 +30,18 @@ let lex_file file =
|
||||
let tokens (file : string) : (Token.t list, error list) result =
|
||||
match lex_file file with Ok ts -> Ok ts | Error e -> Error [ e ]
|
||||
|
||||
let parse_file file =
|
||||
match lex_file file with
|
||||
| Error e -> Error e
|
||||
| Ok tokens -> (
|
||||
match Parser.parse_result tokens with
|
||||
| Ok m -> Ok m
|
||||
| Error { pos; msg } ->
|
||||
Error { file; line = pos.line; col = pos.col; message = msg })
|
||||
|
||||
let ast (file : string) : (Ast.modul, error list) result =
|
||||
match parse_file file with Ok m -> Ok m | Error e -> Error [ e ]
|
||||
|
||||
let check (files : string list) : (unit, error list) result =
|
||||
match files with
|
||||
| [] ->
|
||||
@@ -37,12 +49,12 @@ let check (files : string list) : (unit, error list) result =
|
||||
| _ ->
|
||||
let errors =
|
||||
List.filter_map
|
||||
(fun f -> match lex_file f with Ok _ -> None | Error e -> Some e)
|
||||
(fun f -> match parse_file f with Ok _ -> None | Error e -> Some e)
|
||||
files
|
||||
in
|
||||
if errors <> [] then Error errors
|
||||
else
|
||||
(* 어휘 분석은 통과했다. 통과했다고 말하지 않는다 — 파이프라인의
|
||||
(* 구문 분석은 통과했다. 통과했다고 말하지 않는다 — 파이프라인의
|
||||
나머지가 아직 없으므로 검사되지 않은 것이다. *)
|
||||
Error
|
||||
(List.map
|
||||
@@ -51,7 +63,7 @@ let check (files : string list) : (unit, error list) result =
|
||||
file = f;
|
||||
line = 0;
|
||||
col = 0;
|
||||
message = "어휘 분석까지 통과. 파서가 아직 구현되지 않았습니다";
|
||||
message = "구문 분석까지 통과. 이름 해소가 아직 구현되지 않았습니다";
|
||||
})
|
||||
files)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user