Files
doslang-mirror/audits/2026-08-17-spec-fec-parser.md
coolguy 730bcac282 audit: SPEC-파서 괴리 일곱을 정리한다
구현 셋, SPEC 다섯. 어느 쪽이 틀렸는지는 항목마다 따로 판정했다.

구현:
- ~ 를 넣었다. ^ 는 xor 과 .^ 가 가져가서 비트 NOT 이 자기 철자를 못 갖고
  있었다. 렉서·파서·검사·lowering(전부 1 과의 xor).
- 전역 static/var 는 타입을 적는다. 다른 유닛이 읽는 링커 심볼이라 초기값의
  생김새에 타입을 맡기면 그쪽이 보는 것이 달라진다. const 는 그대로 추론한다.
- error code 는 정수 리터럴 하나다. 식이면 중복도 예약된 0 도 검사할 수 없다.

SPEC:
- 최상위 comptime if 를 뺐다. comptime 조건은 타입 술어뿐인데(§7.5) 유닛
  바깥에는 바인딩된 타입 파라미터가 없어 물어볼 것이 없다. §11 v0.2.
- 타입 이름은 [binding.]Name 이다. import 가 마지막 segment 를 바인딩하므로
  점 둘 이상은 만들어질 수 없는데 문법이 unit_path 를 쓰고 있었다.
- catch 의 EBNF 가 §4.6 보다 넓었다. 값을 주는 짧은 형태와 에러를 받는 블록
  형태 둘로 나눠 적었다.
- | 와 ^ 를 한 단계로 둔 것을 쪼갰다. 합치면 a | b ^ c 가 좌결합으로
  (a|b)^c 가 되어 C 에서 온 사람을 속인다. 구현이 C 순서로 옳았다.
- 마지막 필드 쉼표 생략을 명세에 적었다. enum 은 이미 허용하고 있었다.

그리고 tests/run.py 가 마커를 진단 스트림에만 맞춘다. --dump-ast 모드에서는
AST 덤프가 stdout 으로 먼저 나와서 parse/ fixture 는 마커를 쓸 수 없었다.

249/249, 39/39.
2026-08-17 16:52:12 +09:00

3.3 KiB
Raw Permalink Blame History

SPECfec parser audit

  • 날짜: 2026-08-17
  • 기준 커밋: 52aaff62e490e37a0995aaaf7cbda47cf98e54a7
  • 범위: SPEC.md §6과 fec/src/lexer.c, fec/src/parser.c
  • 해결: 일곱 전부. 구현 셋(PARSE-04a·05·06), SPEC 다섯(01·02·03·04b·07). 판정과 근거는 아래 표에 덧붙였다. 본문은 조사 시점 그대로다.

현재 문제

ID SPEC 현재 구현 재현 결과
PARSE-01 최상위 comptime if 선언 허용 comptime if는 문장에서만 처리 최상위 사용을 expected declaration으로 거부
PARSE-02 타입 이름은 ident ('.' ident)* 타입에서 점 하나만 처리 alpha.beta.Gamma를 파싱하지 못함
PARSE-03 catch EBNF가 binding 없는 block과 binding 뒤 expression도 허용 짧은 catch exprcatch |e| block만 처리 구현은 §4.6 설명과 맞고 §6.1 EBNF가 지나치게 넓음
PARSE-04 |^는 같은 우선순위, 단항 비트 NOT은 ~ 각각 우선순위 5와 6, ~ 토큰 없음, 단항 ^ 허용 ~x를 거부하고 a | b ^ ca | (b ^ c)로 파싱
PARSE-05 전역 staticvar의 타입 필수 타입 표기를 선택적으로 처리하고 초기값에서 추론 static A = 1;, var B = 2; 모두 검사 통과
PARSE-06 error code는 정수 literal 일반 expression을 파싱하며 literal이 아니면 code 검증을 건너뜀 error E { Bad = 1 + 2, }가 검사 통과
PARSE-07 struct field와 enum vfield의 쉼표 필수 닫는 } 바로 앞에서는 쉼표 생략 허용 struct S { x: i32 }가 검사 통과

해결

ID 어느 쪽이 틀렸나 무엇을 했나
PARSE-01 SPEC comptime 조건은 타입 술어뿐이라(§7.5) 유닛 바깥에는 물어볼 것이 없다. comptime_decldecl 에서 빼고 §11 v0.2 로
PARSE-02 SPEC import 는 unit path 의 마지막 segment 를 바인딩하므로 점 둘 이상인 타입 이름은 만들어질 수 없다. 문법을 type_name := [ident '.'] ident
PARSE-03 SPEC §4.6 과 §11(블록 표현식 배제)이 실제 규칙이고 EBNF 가 넓었다. 두 형태로 나눠 적었다
PARSE-04a 구현 ~ 를 넣었다. 렉서·파서·검사·lowering(xor with all ones)
PARSE-04b SPEC |^ 를 한 단계로 두면 a | b ^ c(a|b)^c 가 되어 C 에서 온 사람을 속인다. 구현(C 순서)이 옳아서 표를 쪼갰다
PARSE-05 구현 전역 static/var 는 타입 필수. const 는 그대로 추론
PARSE-06 구현 error code 는 정수 리터럴 하나만 받는다
PARSE-07 SPEC 마지막 쉼표 생략은 흔하고 enum 은 이미 허용하고 있었다. 명세에 적었다

fixture: parse/badgtype.fe, parse/badecode.fe, parse/okglobal.fe, exec/bitnot.fe. 그리고 tests/run.py 가 마커를 진단 스트림에만 맞춘다 -- --dump-ast 모드에서 AST 덤프가 먼저 나와 마커가 못 쓰이고 있었다.

검증

  • uv run python tests/run.py: 240/240 통과
  • 위 항목의 최소 입력을 현재 fec에 직접 넣어 파싱 및 --check 결과를 확인함
  • 기존 fixture에는 위 괴리를 직접 고정하는 사례가 없음

이 문서는 조사 시점의 구현 상태를 기록한다. 언어 규칙의 기준은 SPEC.md다.