Files
doslang-mirror/AGENTS.md
T
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

4.6 KiB

doslang 작업 규칙

DOS/Windows용 시스템 프로그래밍 언어 Ferro와 그 컴파일러 fec. 규범 문서는 SPEC.md이며 이 파일은 그것을 구현할 때의 작업 규칙만 다룬다.

문서 지도

파일 역할
SPEC.md 언어 명세. 유일한 규범 문서
IR.md 중간 표현. 프론트엔드와 기계 사이
TODO.md 남은 작업과 정해진 것. 언어 규칙은 SPEC.md 를 가리키기만 한다
fec/tests/*/README.md 각 fixture 디렉터리가 무엇을 검사하는지
audits/<날짜>-<주제>.md 그때 조사해보니 어땠는지. 불변 기록

파이프라인

.fe  →  fec  →  i386 asm  →  wasm  →  wlink  →  .exe
        └ lexer parser resolve types own check  (프론트엔드)
        └ lower                                 (IR)
        └ x86                                   (백엔드)

wasmwlink는 고정된 Open Watcom의 어셈블러와 링커다 (WebAssembly와 무관). SPEC.md §1 철학 6: 링커와 오브젝트 포맷을 새로 만들지 않는다.

검증

uv run python tests/run.py     # 컴파일러가 프로그램에 대해 뭐라고 하는가
uv run python tests/exec.py    # 컴파일된 프로그램이 실제로 무엇을 하는가
uv run python tests/build.py <프로그램.fe>   # 하나만 빌드해서 돌려보기
  • 두 스위트를 모두 통과해야 한다. run.py만 보면 진단은 옳은데 코드가 안 나오는 상태를 놓친다. 보고만 되고 방출되지 않는 경계 검사가 그 예다.
  • 완료하려는 기능을 직접 검사하는 fixture가 통과해야 한다. 테스트가 증명하지 않는 기능은 완료로 처리하지 않는다.
  • 거부를 기대하는 fixture는 첫 줄에 // ERROR:<줄>:<문구> 마커를 둔다. 마커가 없으면 파일 이름이 기대값이 된다 — bad로 시작하면 거부, 아니면 통과.
  • 실행 프로그램은 첫 줄들에 // EXIT:<코드>, // OUTPUT:<문구>, // NOCHECKS:<코드>를 둔다. 마지막 것은 --no-checks로 다시 빌드해서 다른 결과를 요구한다.
  • 툴체인은 .dosboxx/watcom에 고정되어 있고, 없으면 오류로 멈춘다.

함정

  • 표준 라이브러리는 프로그램이 아니라 컴파일러 옆에 있다. --std=<디렉터리>로 넘기며, 그 디렉터리 안에 std/가 있어야 한다.
  • 유닛 경로의 각 segment는 소문자로 시작하고 a-z0-9_만, 최대 8자다. 파일 경로와 정확히 대응한다 (std.io<std>/std/io.fe).
  • extern "c" fn은 이름을 그대로 쓴다. 나머지는 fe_<유닛>_<이름>으로 맹글링하며 어셈블러가 받지 않는 문자는 밑줄이 된다.
  • 슬라이스 배치(포인터 다음 길이)와 wrapper 페이로드 위치는 각각 한 군데에만 적혀 있다. 두 군데가 되면 어긋난다.

파일 크기

2,000 줄을 넘기지 않는다. 웬만하면 1,000 줄. 넘어가면 나눈다. 나눌 때는 줄 범위로 자르고 -- 주제별로 묶는 것보다 정확하다, 한 줄도 잃거나 겹치지 않으니 -- 공유하는 것은 비공개 헤더(checkpri.h, lowerpri.h)에 모은다.

조사 기록

한 번 조사하고 끝나는 것 -- 명세와 구현의 대조, 진단 증거 수집, 외부 감사 -- 은 audits/<날짜>-<주제>.md에 남긴다. 날짜와 기준 커밋을 적는다. 조사에서 나온 결론SPEC.mdTODO.md로 옮기고, audit 자체는 그때 무엇을 봤는지의 기록으로 둔다.

본문은 고치지 않는다. 다만 해결되면 맨 위에 해결 줄 하나를 붙인다 -- 어느 커밋에서 어떻게 정리됐는지. 그것 없이는 읽는 사람이 아직 살아있는 문제인지 알 수 없다.

계획 문서는 두지 않는다 -- 끝난 계획은 git log 다.

작업 흐름

  • 명세 판단이 바뀌면 SPEC.md를 즉시 갱신한다. 구현이 명세와 다르면 둘 중 하나가 틀린 것이므로 그 자리에서 결론을 낸다.
  • 언어 규칙을 완화하려거든 먼저 프로그램 쪽을 고쳐본다. 규칙이 진짜 언어를 못 쓰게 만들 때만 규칙을 건드리고, 무엇을 왜 바꿨는지 TODO.md에 남겨 사람이 판단하게 한다.
  • 코드는 컴파일러 단계로 나눈다. 마일스톤 단위 분할은 폐기했다.
  • 검증된 단위마다 커밋한다. primary 브랜치는 master다.
  • .dosboxx/의 다운로드, 실행 작업공간, 로그는 커밋하지 않는다.

현재 상태

두 스위트의 통과 수가 현재 상태다. 남은 작업은 TODO.md에 있다.