Commit Graph
22 Commits
Author SHA1 Message Date
coolguy b4f947b643 audit: 프런트엔드 빈틈 열하나와 진법 리터럴
전부 구현 쪽이었다. SPEC 은 §6.2(체이닝·단항 뒤 as), §5 R9(asm), §6.1·§7.3
(extern, 빈 enum/error)을 이미 옳게 적고 있었고 구현만 따라가지 않았다.

  const A: i32 = r();        전역 초기값에 호출. lowering 이 조용히 버리고 0
  static A: i32 = r();       같은 것을 저장소 0 으로
  true == false == true      비교 체이닝
  -x as u32                  괄호 없이 단항 뒤 as
  asm { }                    unsafe 밖에서
  extern fn f();             ABI 문자열 없이
  extern "stdcall" fn f();   c 아닌 ABI
  extern "c" fn f() { }      extern 에 본문
  fn f() -> i32;             extern 아닌데 본문 없음
  enum E { }  error E { }    빈 선언

FRONT-01/02 는 SPEC 에도 규칙이 없었다. §7.1 에 넣었다 -- 전역의 바이트는
이미지에 들어가므로 초기값이 실행될 순간이 없다. 리터럴과 다른 const, 배리언트,
error.Name, 그리고 그것들에 대한 연산까지가 허용된다.

-x as T 와 비교 체이닝을 잡으려면 괄호가 트리에 남아야 해서 FE_NODE_PAREN 을
두었다. 파싱 뒤에는 -x as T 와 -(x as T) 가 같은 트리다.

그리고 0b1010 이 0, 0o17 이 0 이었다. 10진으로 읽다가 b 에서 멈춰 0 을 내는데
0 도 숫자라 아무도 눈치채지 못한다. 값 계산 두 군데를 고쳤다.

262/262, 40/40.
2026-08-17 17:00:29 +09:00
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
coolguy 6dc298d828 docs: 문서 하나가 질문 하나에 답하게
GOAL.md 는 계획일 때 값어치가 있었는데 완료 보고로 바꾸는 순간 git log 의
중복이 됐다. 끝난 계획은 문서가 아니라 히스토리다. 내용은 이미 TODO 와 SPEC
에 들어갔으니 지운다.

fixture-report.md 도 지운다. 기준선이 150/188 인 일회성 조사 스냅샷이고
지금은 245/245 다. 필요하면 2f140e1 에 있다.

TODO 의 '정해진 것' 이 두 종류를 섞고 있었다. 언어 규칙은 SPEC 문장을 옮겨
적은 것이라 한쪽만 고치면 갈라지고, 구현 결정은 SPEC 에 없어서 여기가 유일한
출처다. 둘로 쪼갰다 -- 앞의 표는 §번호만 담고, 뒤의 표는 내용과 그것이 사는
파일을 담는다.

SPEC 의 §7.9 를 §7.7 로 옮겼다. 7.7 과 7.8 없이 7.9 가 떠 있었다.

1847 → 1173 줄. 245/245, 38/38.
2026-08-17 16:40:50 +09:00
coolguy 63baa4f523 GOAL P3-3: List 에 pop take swap slice slice_mut clear
컴파일러가 실제로 쓰는 나머지 표면이다. take 는 R7 대로 대체값을 남기고
꺼내므로 리스트에 구멍이 생기지 않는다.

slice() 를 쓰려면 언어가 한 걸음 필요했다. &Self 로 읽어도 필드가 ^[]mut T
이니 슬라이스가 []mut T 로 나오는데, 선언한 반환은 []T 다. 호출 인자 자리의
약화만 있고 반환 자리에는 없었다.

반환 위치의 약화를 허용했다. R8 이 이미 그 파생을 허용한 뒤라면 []mut T 를
[]T 로 넘기는 것은 가진 것보다 적게 넘기는 일이라 새 별칭을 만들지 않는다.
&Self 메서드가 자기가 소유한 것의 읽기 전용 뷰를 내주는 길이 이것뿐이다.
SPEC §4.2 에 적었고, let 은 여전히 안 된다는 것을 badletwk 가 고정한다.

243/243, 37/37.
2026-08-17 16:22:11 +09:00
coolguy ef7939f609 GOAL P2: SPEC 에서 죽은 C 백엔드를 걷어낸다
파이프라인은 .fe → i386 asm → wasm → wlink → .exe 인데 명세는 아직 C 를
방출하는 컴파일러를 서술하고 있었다. 외부 감사가 그 문서를 충실히 읽고
존재하지 않는 문제(C 의 부호 있는 오버플로 UB)를 보고했다 -- 명세가 거짓말을
하면 그것을 읽는 사람이 틀린 답을 낸다.

걷어낸 것: .fei 심볼 파일과 그 위에 얹힌 증분 빌드 서술, fe_errors.h,
--emit-c 와 --error-table, '호스트 C 방출', 'C 방출 시 static inline',
'별도 C 표현'.

오류 코드 절은 실제대로 다시 썼다: 빌드 하나가 모든 유닛의 소스를 함께 읽고,
드라이버가 emit 전에 쓰인 이름을 모아 철자순으로 1부터 매긴다. 유닛 하나만
따로 코드 생성까지 밀고 갈 수 없다는 것도 그 결과로 적었다.

R7 이 컴파일러 소스 파일 이름(own.c)을 대고 있던 것도 언어의 말로 바꿨고,
이동은 변수 단위이고 대여는 place 단위라는 구분을 붙였다.

--target= 과 --model= 은 드라이버에서 없앴다. 타깃이 하나인데 받아들여서
무시하는 플래그는 안 받는 것보다 나쁘다.

SPEC 이 약속만 하고 구현이 없는 것 셋을 TODO 에 적었다: --strip-error-names,
fmt.fmt_error, 0b/0o 리터럴 값 계산.

240/240, 35/35.
2026-08-17 16:12:53 +09:00
coolguy d9bced830c GOAL P1-4: 작은 규칙 일곱을 정하고, 어긋난 둘을 고친다
일곱 중 다섯은 구현이 이미 옳게 답하고 있었다 -- match 는 enum 전용이고,
enum payload 에 소유 타입을 담을 수 있고, undefined 배열은 원소 단위로
추적하지 않고, 다른 유닛에서 private 필드가 있는 리터럴은 거부되고,
by-value self 의 부분 이동도 mem.replace 가 필요하다. SPEC §7.9 에 적었다.

나머지 둘은 그냥 통과하고 있었다.

defer { return 1; } 이 받아들여졌다. 지연 블록은 함수가 무엇을 반환할지 이미
정한 뒤 스코프 정리 중에 도는데 거기서 return 이 뜻할 것이 없다.

for x in xs { xs[0] = 9; } 도 받아들여졌다. x 가 xs 안을 가리키는 참조이니
순회 몸통에서 xs 에 쓰는 것은 그 참조가 가리키는 것을 옮기는 일이다 (R6).
순회는 이제 도는 동안 대여한다. 읽기는 공유 순회에서 그대로 된다.

240/240, 35/35.
2026-08-17 16:10:44 +09:00
coolguy 1a368dc13f GOAL P1-1..1-3: 리터럴이 자기 타입에 안 맞으면 거부한다
그리고 그 자리를 파다가 더 나쁜 것이 나왔다.

store 의 폭이 목적지가 아니라 값에서 왔다. 정수 리터럴은 더 좁은 것이
요구하기 전까지 i32 이므로 let b: u8 = 200; 은 4바이트가 1바이트 자리로
가는 것으로 도착하고, 4바이트를 쓰면 프레임이 그 옆에 놓은 것을 지운다.

  let a: i32 = 5;  let b: u8 = 300;  let d: u8 = 44;
  a 0 / b 0 / d 44        →   a 5 / b 44 / d 44

폭 넓은 지역 하나만 있으면 드러나지 않아서 여태 살아 있었다. exec/narrow.fe
가 폭이 섞인 지역을 나란히 두어 고정한다.

규칙 자체는 SPEC §3 에 넣었다: 리터럴의 타입은 문맥이 요구하는 정수 타입이고,
없으면 i32 다. 범위를 벗어나면 잘리는 것이 아니라 거부된다. 앞의 단항 -
는 리터럴의 일부로 보아 i8 = -128 은 되고 u8 = -1 은 안 된다.

같이 넣은 문장 둘:
- §9 미사용 타입 파라미터는 정상이다. typed handle 이 그 모양이고 구현은
  이미 그렇게 동작했다.
- §7.4 --no-checks 에서 오버플로는 랩어라운드로 정의된다. 타깃이 실제로
  하는 일이고 미정의로 두지 않는다.

237/237, 35/35.
2026-08-17 16:07:45 +09:00
coolguy f7e667652e 대여는 변수가 아니라 place 단위다
p.a 와 p.b 는 서로 다른 자리인데 한쪽을 대여하면 다른 쪽까지 잠겼다. 메서드가
하는 일의 대부분이 한 필드에 쓰면서 다른 필드를 읽는 것이라, std.map 의 keep 은
그것 때문에 함수 둘로 갈라져 있었고 파서도 같은 자리에서 걸렸다.

FeOwnState 가 필드별 칸을 넷 갖는다. 값으로 복사되는 구조체라 흐름 병합과
스냅샷은 손댈 것이 없었다. 전체를 대여하면 모든 필드와 충돌하고, 필드를
대여하면 전체 및 같은 필드와 충돌한다. 칸이 모자라면 전체 대여로 되돌아가
더 많이 보고할 뿐 놓치지 않는다.

읽기는 식별자에서 일어나는데 그 자리에서는 자기가 무엇의 밑동인지 알 수 없다.
그래서 투영이 내려가는 길에 어느 필드인지 적어두고 식별자가 그것을 집는다.
인덱스는 갈라지지 않는다 -- xs[i] 의 i 는 상수가 아닐 수 있고, 필드 이름은
상수다.

길에서 나온 것: mem.replace 가 목적지 대여를 가져가고 돌려주지 않았다. 일반
호출 인자는 문장 끝에 돌려주는데 intrinsic 경로에만 그것이 없었다. 전에는
그 자리가 어차피 거부돼서 드러나지 않았다.

  var p = Pair{ a: 1, b: 2 };
  let r = &mut p.a;
  p.b = 3;      // ok -- 전에는 에러
  p.a = 3;      // 에러
  take(p);      // 에러

SPEC §5 R6 을 고쳤고, 옛 규칙을 그대로 적어둔 문단과 예제를 갈아치웠다.
own/badrfld 는 이제 허용되는 코드였으므로 같은 필드를 건드리도록 다시 겨눴고
okrfld·badrall·badrsame·exec/fieldbrw 를 더했다.

228/228, 32/32.
2026-08-17 15:29:12 +09:00
coolguy 120a36eef5 spec: 완화했던 이동 규칙 둘을 명세에 넣는다
구현만 알고 있으면 둘 중 하나가 틀린 것이다.

- 배타 대여를 호출에 넘기는 것은 재대여 -- 이미 §4.2 에 있었다.
- 자기 drop 안의 부분 이동은 R7 예외다. 객체가 사라지는 중이라 drop 이 돌아간
  뒤에 그 반쪽짜리 값을 읽을 코드가 없다. 다른 함수에는 예외가 없다.

TODO 의 '판단을 기다리는 것' 이 비었다.
2026-08-17 15:16:24 +09:00
coolguy 432d073104 lang: 배타 대여를 호출에 넘기는 것은 이동이 아니라 재대여다
&mut T 를 &mut T 파라미터에 넘기면 호출이 끝날 때 돌려받는다. 호출이 도는
동안 호출자는 그 값에 손댈 수 없으므로 별칭이 생기지 않는다. 이것이 없으면
배타 파라미터를 다시 넘기는 일이 함수당 한 번만 가능해서 &mut 가 사실상 쓸 수
없었다 -- 재귀 하강 파서를 쓰다가 걸렸다.

페이로드 없는 enum 은 이름 붙은 수라서 수로 읽을 수 있다. 반대 방향은 안 된다:
임의의 수는 변이가 아니다.

calc 프로그램: 재귀 하강 수식 계산기. 우선순위, 괄호, 오류 전파.

  1+2*3 = 7   (1+2)*3 = 9   2*(3+4)-5 = 9   10/3 = 3
  1+ = error  (1+2 = error
2026-08-17 06:45:19 +09:00
coolguy 718c323938 spec: 타깃을 i386 하나로 정하고 far 를 언어에서 뺀다
세그먼트 주소 지정은 x86 리얼모드에만 있는 개념이고, 평평한 주소 공간을 가진
다른 32비트 프로세서에는 대응물이 없다. 영구 제외다.

usize/isize 는 타깃의 포인터 폭이며 특정 비트 수를 약속하지 않는다. 오늘
32비트지만 u32 와 자동 변환되지 않는다. bits16 에서 배울 것은 '32로 정하자'가
아니라 '폭이 언어 의미론으로 새어나가지 않게 하자'이고, 그것이 나중에 다른
폭의 타깃을 여는 유일한 장치다.

타깃이 하나이므로 레이아웃에서 pointer_bits 분기가 전부 사라졌다. 인터럽트
핸들러와 공유 상태는 v0.2 로 내렸다 -- 문법은 아직 파싱되지만 명세의 약속은
아니다.
2026-08-17 05:40:49 +09:00
coolguy c900e0f61c spec: 640KB 셀프호스팅을 목표에서 내린다
철학 2의 근거가 사실과 달랐다. 전역 분석 금지만으로는 메모리가 줄지 않고,
현재 프론트엔드는 이미 그 예산을 한 자릿수 넘겼다 — FeBuild 하나가 26,892바이트
스택 지역 변수이고, 유닛 64개의 소스와 AST를 동시에 들고 있다.

전역 분석 금지는 국소적 진단과 작은 컴파일러라는 자체 근거로 유지한다.
컴파일러가 도는 곳을 §2.1로 분리했다. bits16 타깃은 그대로다 -- 8086용
프로그램을 만드는 것과 8086에서 컴파일러를 돌리는 것은 다른 일이다.
2026-08-17 04:39:58 +09:00
coolguy 2696dd2abc docs: reduce SPEC.md to a language-only specification
Abandon the milestone-driven organisation of SPEC.md. The document now
contains only design philosophy (§1) and the language specification
proper (lexical structure, types, ownership/borrow rules, grammar,
semantics, module/unit semantics, and the minimal stdlib surface the
language itself depends on).

Removed:
- All milestone content (M1-M12: descriptions, completion criteria,
  ordering) and the roadmap/schedule framing.
- Compiler implementation directives: bootstrap strategy, pipeline,
  directory layout, C emission rules, own.c algorithm (§11 in full).
- Test/fixture plans and pass/fail/run16/boot fixture listings (§12
  in full).
- Build-driver/tooling detail that isn't part of the language itself:
  import-root search and ambiguity resolution, .fei cache/hash format,
  the full CLI flag reference table, C-backend evaluation-order
  lowering notes, and the generic-instance C emission ordering.
- SPEC.AUDIT.md entirely (git rm) — the accumulated change log for the
  old milestone-driven spec no longer applies.

Kept and reorganised: §2-§9 (targets, lexical structure, type system,
ownership/borrow rules R1-R11, grammar, statement/expression
semantics, unit/import/visibility semantics, generics) are otherwise
unchanged in wording. §10 (stdlib) is now a short placeholder noting
the stdlib spec is pending, while retaining the minimal surface the
language rules and builtins directly reference (core.Error, str alias
methods, mem.replace/create/destroy/alloc_slice, io.Writer/Reader,
fmt.fmt_*, sys.exit/on_exit). §13 (excluded features) is renumbered to
§11 and kept as-is since it documents language-design decisions, not
implementation.

AGENTS.md's document map is updated to drop the SPEC.AUDIT.md row and
reflect that SPEC.md is now language-only.

Implementation and stdlib specs are intended to be written as separate
documents going forward.
2026-08-17 02:42:12 +09:00
coolguy 3de0564650 freeze M6-M9 semantics and unit model 2026-08-16 19:48:19 +09:00
coolguy f798a1359f docs: resolve ownership and standard library spec audit 2026-08-16 17:29:45 +09:00
coolguyandClaude Opus 5 41de8aa2dc docs: revise spec to v0.1.6 and drop stale handoff
R8을 파생 반환 규칙으로 교체해 str.trim 계열의 슬라이스 반환을 표현 가능하게
하고, 결과를 지역 변수에 바인딩할 수 있게 한다. R6의 대여 구간을 마지막 사용
지점까지로 좁히고, R10에 전역 대여 금지를 추가해 호출 경계에서 배타성이 깨지던
구멍을 막는다. error.Name 코드 부여를 링크 심볼에서 드라이버의 emit 이전 단계로
옮겨 컴파일타임 상수로 만들고, str을 []u8과 별개 타입으로 분리한다.

문법 결함(struct 멤버의 pub, enum 배리언트 필드, catch/orelse 프로덕션,
error_decl, expr 규범)과 CLI 플래그 산재, 문서-구현 드리프트를 함께 정리한다.
변경 사유는 SPEC.AUDIT.md에 기록했다.

HANDOFF.md는 기준 SHA와 통신 프로토콜이 모두 낡아 제거한다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012PQm6oAvWX4Lp3iSN5AHGT
2026-08-16 16:54:34 +09:00
coolguy 57b47a574a docs: disambiguate control-flow headers 2026-08-16 08:42:21 +09:00
coolguy 95de333da4 docs: disambiguate match scrutinees 2026-08-16 08:33:19 +09:00
coolguy 3aa7618d0c docs: clarify char and byte conversions 2026-08-16 08:16:24 +09:00
coolguy 990068f424 docs: resolve language audit for Ferro v0.1.2 2026-08-16 06:44:39 +09:00
coolguy c7b0217d43 docs: define formatting builtins and interface roadmap 2026-08-16 06:16:03 +09:00
coolguy cbfad06e6f Initial DOS VM tooling setup 2026-08-16 06:12:59 +09:00