docs: resolve language audit for Ferro v0.1.2
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# Ferro 언어 명세 v0.1
|
||||
# Ferro 언어 명세 v0.1.2
|
||||
|
||||
DOS용 시스템 프로그래밍 언어. C만큼 빠르고, 메모리 안전성을 함수 단위 지역 검사만으로 보장한다.
|
||||
파일 확장자 `.fe`, 컴파일러 이름 `fec`, 심볼 파일 `.fei`.
|
||||
@@ -28,7 +28,7 @@ DOS용 시스템 프로그래밍 언어. C만큼 빠르고, 메모리 안전성
|
||||
| 메모리 모델 | small, large | flat |
|
||||
| 시스템 호출 | INT 21h 직접 | DPMI 서비스 + 실모드 콜백 |
|
||||
|
||||
- CLI: `fec main.fe --target=bits16|bits32 [--model=small|large]`
|
||||
- CLI: `fec main.fe --target=bits16|bits32 [--model=small|large] [--strip-error-names]`
|
||||
- 소스 분기: `comptime if @bits == 16 { ... } else { ... }`
|
||||
- `bits32`에서 `far` 키워드를 쓰면 컴파일 에러.
|
||||
- 표준 라이브러리는 코어 공용, `sys` 유닛만 타깃별 구현.
|
||||
@@ -47,10 +47,11 @@ DOS용 시스템 프로그래밍 언어. C만큼 빠르고, 메모리 안전성
|
||||
|
||||
**예약어:**
|
||||
```
|
||||
unit import pub fn struct enum error const static var let mut
|
||||
unit import pub fn struct packed enum error const static var let
|
||||
if else while for in match return break continue defer
|
||||
unsafe comptime asm try catch as extern interrupt far
|
||||
true false null self Self type
|
||||
unsafe critical shared atomic comptime asm try catch as extern interrupt interrupt_safe far
|
||||
true false null undefined self Self type
|
||||
and or not orelse
|
||||
```
|
||||
|
||||
---
|
||||
@@ -164,6 +165,21 @@ fn read_all(path: str) -> IoError!^[]u8 {
|
||||
- `e catch default_value`: 짧은 형태.
|
||||
- 서로 다른 error 타입 간 자동 변환 없음. `!T`(기본 에러 집합 `core.Error`)로 통일하거나 명시 매핑.
|
||||
- 에러는 값이다. 언와인딩, 스택 추적, 소멸자 이외의 자동 정리 없음.
|
||||
- 실패를 복구하지 않고 트랩으로 바꾸려면 `expr catch @trap()`을 쓴다. v0.1.2에는 별도 `must` 키워드를 두지 않는다.
|
||||
|
||||
`error.Name`은 선언된 error 타입을 만들지 않고 기본 `core.Error`의 이름 있는
|
||||
멤버를 참조하는 익명 에러 값이다. 각 유닛은 사용한 이름을 `.fei`에 기록한다.
|
||||
최종 빌드에서 모든 유닛의 이름을 합치고 중복을 제거한 뒤 이름의 바이트순으로
|
||||
정렬하여 1부터 안정적인 `u16` 코드를 부여한다. 따라서 서로 다른 유닛의
|
||||
`error.Name`은 같은 값이고 빌드 순서와 병렬 컴파일에도 결과가 결정적이다.
|
||||
코드는 최종 링크용 생성 헤더의 심볼로 참조하므로 분리 컴파일에서도 일관된다.
|
||||
이름이 65,535개를 넘으면 컴파일 에러다. 명시적인 `error` 선언은 여전히 nominal
|
||||
타입이며, 같은 멤버 이름이나 숫자 코드를 가진 다른 선언 및 `core.Error`와 자동
|
||||
변환되지 않는다. `error.Name`의 타입은 `core.Error`이며 `core.Error!T` 또는
|
||||
축약형 `!T`를 반환하는 함수에서만 직접 반환할 수 있다.
|
||||
`--strip-error-names`를 사용하면 실행 파일과 런타임 오류 문자열에서 이름을
|
||||
제거하지만 숫자 코드와 `.fei`의 타입/코드 일관성 정보는 유지한다.
|
||||
`fmt.write_error`는 이 정책에 따라 `core.Error` 값을 이름 또는 코드로 출력한다.
|
||||
|
||||
### 4.7 타입 동등성
|
||||
|
||||
@@ -179,7 +195,7 @@ fn read_all(path: str) -> IoError!^[]u8 {
|
||||
|
||||
**R2 (Copy 타입).** 다음은 이동 대신 복사된다: 정수, `bool`, `char`, raw 포인터 `*T`, 참조 `&T`, 함수 포인터, 그리고 모든 필드가 Copy이면서 `drop`이 없는 struct/enum/배열. `^T`와 `&mut T`는 Copy가 아니다.
|
||||
|
||||
**R3 (소멸자, RAII).** `^T`는 소유자 스코프 종료 또는 재대입 시 `drop` 호출 후 해제. struct에 `fn drop(self: &mut Self)`가 있으면 그 값의 스코프 종료 시 자동 호출되며, 이어서 필드들의 drop이 선언 역순으로 호출된다. `drop`을 직접 호출하는 것은 컴파일 에러(`mem.destroy(x)` 사용).
|
||||
**R3 (소멸자, RAII).** `^T`는 소유자 스코프 종료 또는 재대입 시 `drop` 호출 후 해제. struct에 `fn drop(self: &mut Self)`가 있으면 그 값의 스코프 종료 시 자동 호출되며, 이어서 필드들의 drop이 선언 역순으로 호출된다. `drop`을 직접 호출하는 것은 컴파일 에러(`mem.destroy(x)` 사용). `^Self` 또는 `?^Self`를 재귀적으로 포함한 타입은 기본 필드 drop이 스택 깊이에 비례할 수 있으므로 컴파일러가 경고한다. 이런 연결 구조는 `mem.replace(&mut link, null)`로 소유 링크를 하나씩 꺼내 반복 해제하고 필드를 빈 값으로 남기는 사용자 `drop`을 정의해야 하며, `--deny-recursive-drop`으로 경고를 에러로 바꿀 수 있다.
|
||||
|
||||
**R4 (참조는 2급 값).** `&T`, `&mut T`, `[]T`는 다음 위치에만 존재할 수 있다:
|
||||
- 함수 파라미터
|
||||
@@ -209,11 +225,16 @@ let r = list.at(0); // 에러: 반환 참조를 바인딩 불가
|
||||
```
|
||||
장수하는 접근이 필요하면 인덱스(`usize`)나 핸들을 쓴다.
|
||||
|
||||
**R9 (unsafe).** `unsafe {}` 안에서만 허용: raw 포인터 역참조, `*T` ↔ `^T`/`&T` 변환, `@ptr_cast`, `@seg_ptr`, `@volatile_*`, `@port_*`, `asm`, `*_unchecked` 함수, 전역 `var` 접근. R1~R8은 `unsafe` 안에서도 유지되며, raw 포인터를 경유해야만 우회된다.
|
||||
**R9 (unsafe).** `unsafe {}` 안에서만 허용: raw 포인터 역참조, `*T` ↔ `^T`/`&T` 변환, `@ptr_cast`, `@seg_ptr`, `@volatile_*`, `@port_*`, `@as_far_fn`, `@call_far`, `asm`, `*_unchecked` 함수. R1~R8은 `unsafe` 안에서도 그대로 유지된다. 특히 `unsafe`가 참조 반환·저장이나 대여 검사를 끄지 않으며, 프로그래머가 명시적으로 raw 포인터를 경유한 부분만 컴파일러의 메모리 안전 보장 밖에 놓인다.
|
||||
|
||||
**R10 (전역).** `static`은 불변이며 컴파일타임 상수 초기화만 가능. `var` 전역은 허용되나 접근이 `unsafe`. 전역에 `^T`나 `drop` 있는 타입 금지.
|
||||
**R10 (전역과 인터럽트 공유).** `static`은 불변이며 컴파일타임 상수 초기화만 가능하다. 일반 전역 `var`의 읽기와 쓰기는 안전하며 `unsafe`가 필요 없다. 인터럽트 핸들러와 메인 흐름이 함께 접근하는 값은 반드시 `shared var`로 선언하고 다음 규칙을 적용한다.
|
||||
- 메인 흐름은 `critical {}` 안에서만 `shared var`에 접근할 수 있다. 진입 시 플래그를 저장하고 인터럽트를 막으며, 정상 종료·`return`·`break`·`continue`·에러 전파를 포함한 모든 이탈 경로에서 원래 플래그를 복원한다.
|
||||
- `interrupt fn` 안에서는 `shared var`에 직접 접근할 수 있다. 일반 함수와 `interrupt_safe fn`은 호출 문맥을 알 수 없으므로 명시적 `critical` 밖의 비원자 공유 접근이 금지된다.
|
||||
- `shared atomic var`는 타깃에서 한 명령으로 읽고 쓸 수 있는 정수/불린/포인터 스칼라에만 허용한다. 메인 흐름의 단일 읽기·쓰기에는 컴파일러가 필요한 최소 임계 구역을 생성한다. 복합 read-modify-write는 여전히 명시적 `critical {}`이 필요하다. 지원되지 않는 크기나 타입은 컴파일 에러다.
|
||||
- `interrupt fn`은 `interrupt_safe fn`만 호출할 수 있다. `interrupt_safe fn`은 힙 할당, DOS/DPMI 서비스, 블로킹 I/O, 부동소수점, `critical` 및 안전하지 않은 함수 호출을 사용할 수 없으며 컴파일러가 함수 본문만 보고 검증한다. 이 효과는 `.fei` 시그니처에 기록한다.
|
||||
- 전역에는 `^T`나 `drop` 있는 타입을 둘 수 없다. `shared`, `atomic`, `critical`, `interrupt fn`은 v0.1.2에서 `bits16` 전용이며 `bits32`에서 사용하면 컴파일 에러다.
|
||||
|
||||
**R11 (그래프 구조).** 참조 순환이 필요한 자료구조는 아레나 + 인덱스 핸들로 표현한다. 표준 라이브러리 `mem.Arena`와 `u16`/`u32` 인덱스를 쓴다.
|
||||
**R11 (재귀·그래프 구조).** `^T`는 R4의 2급 참조가 아니므로 소유가 한 방향인 단방향 리스트와 트리는 필드에 저장할 수 있다. 반면 양방향 리스트·순환·일반 그래프는 역방향 필드에 `^T`를 두면 R1의 단일 소유권을 위반하고 `&T`를 두면 R4를 위반한다. 이런 구조는 아레나/배열이 값을 소유하고 `u16`/`u32` 인덱스 핸들이 간선을 나타내도록 구현한다. 표준 라이브러리 `mem.Arena`를 사용할 수 있으며, 핸들 역참조 때 세대 번호 또는 경계 검사를 사용해 해제된 항목 접근을 막아야 한다.
|
||||
|
||||
---
|
||||
|
||||
@@ -228,7 +249,7 @@ import := 'import' ident ';'
|
||||
decl := ['pub'] (fn_decl | struct_decl | enum_decl | error_decl
|
||||
| const_decl | global_decl)
|
||||
|
||||
fn_decl := ['extern' string] ['interrupt'] 'fn' ident
|
||||
fn_decl := ['extern' string] [('interrupt' | 'interrupt_safe')] 'fn' ident
|
||||
'(' [param (',' param)*] ')' ['->' type] (block | ';')
|
||||
param := ['comptime'] ident ':' type
|
||||
struct_decl := ['packed'] 'struct' ident '{' field* fn_decl* '}'
|
||||
@@ -237,11 +258,13 @@ enum_decl := 'enum' ident '{' variant (',' variant)* [','] '}'
|
||||
variant := ident | ident '(' type ')' | ident '{' field* '}'
|
||||
error_decl := 'error' ident '{' (ident '=' int ',')* '}'
|
||||
const_decl := 'const' ident [':' type] '=' expr ';'
|
||||
global_decl := ('static' | 'var') ident ':' type '=' expr ';'
|
||||
global_decl := 'static' ident ':' type '=' expr ';'
|
||||
| 'var' ident ':' type '=' expr ';'
|
||||
| 'shared' ['atomic'] 'var' ident ':' type '=' expr ';'
|
||||
|
||||
block := '{' stmt* '}'
|
||||
stmt := 'let' ['mut'] ident [':' type] '=' expr ';'
|
||||
| 'var' ident ':' type ['=' expr] ';'
|
||||
stmt := 'let' ident [':' type] '=' expr ';'
|
||||
| 'var' ident [':' type] ['=' expr] ';'
|
||||
| 'const' ident [':' type] '=' expr ';'
|
||||
| lvalue ('=' | '+=' | '-=' | '*=' | '/=' | '%='
|
||||
| '&=' | '|=' | '^=' | '<<=' | '>>=') expr ';'
|
||||
@@ -249,6 +272,7 @@ stmt := 'let' ['mut'] ident [':' type] '=' expr ';'
|
||||
| 'return' [expr] ';' | 'break' ';' | 'continue' ';'
|
||||
| 'defer' block
|
||||
| 'unsafe' block
|
||||
| 'critical' block
|
||||
| 'comptime' 'if' expr block ['else' (block | 'if' ...)]
|
||||
| 'asm' '{' asm_body '}'
|
||||
| expr ';'
|
||||
@@ -269,6 +293,7 @@ type := ident ['.' ident]
|
||||
| '?' type | '!' type | ident '!' type
|
||||
| '^' type | '&' ['mut'] type | '*' type
|
||||
| 'far' ('^' | '*' | '&' ['mut']) type
|
||||
| 'far' 'fn' '(' [type (',' type)*] ')' ['->' type]
|
||||
| '[' expr ']' type | '[' ']' type
|
||||
| 'fn' '(' [type (',' type)*] ')' ['->' type]
|
||||
| ident '(' type (',' type)* ')' // 제네릭 인스턴스
|
||||
@@ -278,20 +303,21 @@ type := ident ['.' ident]
|
||||
|
||||
```
|
||||
1 orelse, catch
|
||||
2 ||
|
||||
3 &&
|
||||
2 or
|
||||
3 and
|
||||
4 == != < <= > >=
|
||||
5 | ^
|
||||
6 &
|
||||
7 << >>
|
||||
8 + - +% -%
|
||||
9 * / % *%
|
||||
10 단항: - ! ~ & &mut ^(주소아님) try
|
||||
10 단항: - not ~ & &mut ^(주소아님) try
|
||||
11 후위: .field .? .^ [i] [a..b] (args) as T
|
||||
12 기본: literal, ident, '(' expr ')', struct_literal, @builtin(...)
|
||||
```
|
||||
|
||||
- `&&`, `||`는 단축 평가.
|
||||
- `and`, `or`는 단축 평가한다. 호스트 C 방출에서는 각각 `&&`, `||`로
|
||||
매핑하며, 평가 순서와 단락 규칙은 Ferro 의미론을 그대로 유지한다.
|
||||
- `as`는 후위 우선순위(단항보다 강함): `-x as i32`는 `-(x as i32)`.
|
||||
- 비교 연산 체이닝 금지(`a < b < c`는 에러).
|
||||
|
||||
@@ -307,9 +333,12 @@ type := ident ['.' ident]
|
||||
@trap() -> never @unreachable() -> never (unsafe)
|
||||
@line() @file() // 진단용
|
||||
|
||||
@print(fmt, ...) // stdout
|
||||
@fprint(w, fmt, ...) // 임의 Writer
|
||||
@print(fmt, ...) -> void // stdout, 쓰기 오류 무시
|
||||
@fprint(w, fmt, ...) -> !void // 임의 Writer
|
||||
@sprint(buf: []u8, fmt, ...) -> usize // 버퍼에 기록, 쓴 바이트 수 반환
|
||||
@compile_error(msg) // comptime에서 항상 컴파일 에러
|
||||
@as_far_fn(f) -> far fn() // bits16 전용 함수 포인터 변환
|
||||
@call_far(p: far fn()) // bits16/unsafe 전용 호출
|
||||
```
|
||||
|
||||
### 6.3.1 포매팅 빌트인
|
||||
@@ -332,9 +361,21 @@ fmt.write_str(out, "\n");
|
||||
- verb: `{}` 기본(정수/bool/char/str 자동), `{x}` 16진, `{c}` 문자, `{s}` 문자열/슬라이스, `{b}` 불린. `{{`는 `{` 이스케이프.
|
||||
- `{}` 개수와 인자 개수 불일치 → 컴파일 에러.
|
||||
- 인자 타입에 대응하는 `fmt.write_*` 함수가 없으면 컴파일 에러(메시지에 타입명 표시).
|
||||
- 자릿수/폭/정렬 지정자는 v0.1에 없음. 필요하면 `fmt.write_int_pad`를 직접 호출.
|
||||
- 자릿수/폭/정렬 지정자는 v0.1.1에 없음. 필요하면 `fmt.write_int_pad`를 직접 호출.
|
||||
- `@fprint`의 첫 인자는 `&mut io.Writer`(§10의 함수 포인터 struct).
|
||||
- 전개 결과의 모든 호출은 `!void`를 반환하므로 `@print`도 `!void`. 무시하려면 `_ = @print(...)`.
|
||||
- `@print`는 stdout에 기록하며 저수준 writer 오류를 삼키고 `void`를 반환한다.
|
||||
따라서 `try @print(...)`는 컴파일 에러다.
|
||||
- `@fprint`는 writer 오류를 전파하여 `!void`를 반환한다.
|
||||
- `@sprint`는 버퍼가 찬 뒤의 출력이 잘리더라도 트랩하지 않고 기록된 바이트 수를
|
||||
`usize`로 반환한다.
|
||||
- 전개된 `fmt.write_*` 호출은 위 반환 규칙에 맞게 lower 단계에서 오류를
|
||||
전파하거나 무시한다. `fmt.write_error`는 `core.Error`의 이름/코드를 출력한다.
|
||||
|
||||
`@compile_error(msg)`의 `msg`는 comptime 문자열이어야 하며, 평가되는 분기에서
|
||||
항상 진단을 발생시킨다. `comptime if`의 제거되는 분기에서는 진단하지 않는다.
|
||||
`@as_far_fn(f)`와 `@call_far`는 `bits16`에서만 허용된다. 전자는 함수 포인터를
|
||||
`far fn()`으로 변환하고 후자는 `far fn()`을 호출한다. 둘 다 `unsafe { }` 안에서만
|
||||
사용할 수 있으며, `bits32`에서는 컴파일 에러다.
|
||||
|
||||
### 6.4 예제
|
||||
|
||||
@@ -396,7 +437,7 @@ pub fn main() -> !void {
|
||||
|
||||
### 7.1 변수와 초기화
|
||||
|
||||
- `let`은 불변, `let mut`은 가변, `var`는 타입 명시 필수인 가변 선언.
|
||||
- `let`은 불변, `var`는 가변 선언이다. 두 형태 모두 초기값이 있으면 타입을 추론할 수 있다. `var x: T;`와 `var x: T = undefined;`처럼 초기값이 없거나 `undefined`이면 타입 명시가 필수다.
|
||||
- 모든 변수는 사용 전 초기화 필수(정적 검사). 명시적 미초기화는 `= undefined`(unsafe 아님, 단 읽기 전 쓰기 필수는 여전히 검사).
|
||||
- 섀도잉 허용(같은 스코프에서 `let` 재선언).
|
||||
|
||||
@@ -415,7 +456,8 @@ pub fn main() -> !void {
|
||||
|
||||
- 기본: `bits32`는 cdecl, `bits16`은 타깃 C 컴파일러 기본.
|
||||
- `extern "c" fn name(...) -> T;` — 본문 없이 선언, C 심볼과 링크. 이름 맹글링 없음. 인자/반환에 `^T`, 슬라이스, 에러 유니온 사용 불가(`*T`, `usize`만).
|
||||
- `interrupt fn name()` — 모든 레지스터 보존 + `iret`. 파라미터/반환 없음. 주소는 `@as_far_fn(name)`으로 획득.
|
||||
- `interrupt fn name()` — 모든 레지스터 보존 + `iret`. 파라미터/반환 없음. 주소는 `@as_far_fn(name)`으로 획득. 호출 제한과 공유 상태 규칙은 R10을 따른다.
|
||||
- `interrupt_safe fn name(...)` — 인터럽트 문맥에서 호출 가능한 함수. ABI는 일반 함수와 같고 R10의 제한을 본문 검사로 만족해야 한다.
|
||||
- 큰 struct(> 4바이트)는 숨은 포인터로 반환(C ABI 따름).
|
||||
|
||||
### 7.4 검사와 트랩
|
||||
@@ -488,13 +530,15 @@ var xs: List(u8) = List(u8).new();
|
||||
## 10. 표준 라이브러리 (최소 집합)
|
||||
|
||||
- **core**: `panic`, `set_panic_handler`, `Error`(기본 에러 집합), `assert`.
|
||||
- **mem**: `create(T) -> !^T`, `destroy(p)`, `alloc_slice(T, n) -> !^[]T`, `copy(dst, src)`, `set(dst, v)`, `Arena{ init, alloc, reset, drop }`.
|
||||
- **mem**: `create(T) -> !^T`, `destroy(p)`, `alloc_slice(T, n) -> !^[]T`, `replace(dst: &mut T, value: T) -> T`, `copy(dst, src)`, `set(dst, v)`, `Arena{ init, alloc, reset, drop }`. `replace`는 이전 값을 이동해 반환하고 새 값으로 자리를 초기화하며 재귀 구조의 반복 drop에도 사용한다.
|
||||
- **str**: `eq`, `find`, `starts_with`, `split_at`, `parse_int`, `trim`, `to_cstr(buf, s)`, `from_cstr(p)`.
|
||||
- **list**: `List(T)`.
|
||||
- **map**: `Map(K, V)` (오픈 어드레싱, `K`는 정수/str).
|
||||
- **fmt**: `@print` 계열이 전개해 호출하는 저수준 함수 모음.
|
||||
`write_str(w, s)`, `write_int_i8/i16/i32/u8/u16/u32(w, v)`, `write_hex_u8/u16/u32(w, v)`,
|
||||
`write_char(w, c)`, `write_bool(w, b)`, `write_int_pad(w, v, width, pad)`.
|
||||
`write_char(w, c)`, `write_bool(w, b)`, `write_error(w, e)`,
|
||||
`write_int_pad(w, v, width, pad)`. `write_error`는 `--strip-error-names` 설정을
|
||||
따르며, 모든 함수는 `!void`를 반환한다.
|
||||
전부 `fn(w: &mut io.Writer, ...) -> !void` 시그니처. 사용자가 직접 호출해도 된다.
|
||||
- **io**:
|
||||
- `File{ open, create, read, write, seek, size, close(=drop) }`, `stdin`, `stdout`, `stderr`.
|
||||
@@ -589,7 +633,9 @@ fec/
|
||||
- **조건부 이동**: 이동 여부가 분기에 따라 다르면 `unsigned char fe_live_<var> = 1;` 플래그 삽입, drop 전에 검사.
|
||||
- **`match`**: `switch (x.tag)`. 페이로드 바인딩은 지역 변수로 복사 또는 포인터.
|
||||
- **`asm`**: Intel 문법으로 고정 저장. Watcom/Borland는 그대로, gcc는 `__asm__(".intel_syntax noprefix\n" ...)`로 감싼다.
|
||||
- **`@print` 계열**: emit 단계에는 도달하지 않는다. lower 단계에서 이미 `fmt.write_*` 호출 나열로 전개되므로 emit_c는 일반 함수 호출로만 본다. 포맷 문자열 조각은 각각 static const 문자열 리터럴로 방출하고 동일 문자열은 중복 제거.
|
||||
- **`@print` 계열**: emit 단계에는 도달하지 않는다. lower 단계에서 이미 `fmt.write_*` 호출 나열로 전개되므로 emit_c는 일반 함수 호출로만 본다. `@print`는 각 호출의 오류 코드를 명시적으로 버리고 `void`가 되며, `@fprint`는 첫 오류를 전파하고, `@sprint`는 남은 버퍼 길이를 추적해 잘라 쓴 뒤 실제 길이를 반환한다. 포맷 문자열 조각은 각각 static const 문자열 리터럴로 방출하고 동일 문자열은 중복 제거.
|
||||
- **논리 연산**: `and`, `or`, `not`은 각각 C의 `&&`, `||`, `!`로 방출한다. `and`와 `or`는 C의 시퀀스 포인트와 단축 평가를 그대로 사용한다.
|
||||
- **임계 구역**: bits16의 `critical`은 진입 시 FLAGS를 저장한 뒤 `cli`하고 모든 이탈 경로에서 저장한 FLAGS를 복원한다. `shared atomic var`의 단일 접근도 같은 보존형 시퀀스를 사용하며 무조건 `sti`하지 않는다.
|
||||
- **방출 순서**: typedef 전방선언 → struct 정의(의존 위상 정렬) → 전역 → 함수 프로토타입 → 함수 본문.
|
||||
- 유닛 하나당 `.c` 하나, `.fei`에서 필요한 부분은 `.h`로 생성.
|
||||
|
||||
@@ -619,13 +665,13 @@ Uninit | Owned | Moved | MaybeMoved | Shared(n) | Exclusive
|
||||
| M1 | lexer, parser, AST 덤프 | `--dump-ast`가 std 소스 전체를 파싱 |
|
||||
| M2 | 타입 검사 + C 방출: 정수, 함수, if, while | bits32 hello world 실행 |
|
||||
| M3 | struct, enum, match, 배열, 슬라이스, 경계 검사, str | 문자열 처리 예제 통과 |
|
||||
| M4 | **`@print`/`@fprint`/`@sprint` 빌트인** (§6.3.1), `io.Writer` 함수 포인터 struct | 포맷 출력 동작, 인자 개수·타입 불일치가 컴파일 에러 |
|
||||
| M4 | **`@print`/`@fprint`/`@sprint` 빌트인** (§6.3.1), `io.Writer` 함수 포인터 struct | `@print`의 `void` 오류 삼킴, `@fprint`의 `!void` 전파, `@sprint`의 잘림/길이 동작과 인자 개수·타입 불일치가 컴파일 에러 |
|
||||
| M5 | `^T`, drop, defer, 이동 검사 | 누수/이중해제 테스트 통과 |
|
||||
| M6 | `&`, `&mut`, 배타성 검사 (own.c 전체) | R1~R8 실패 테스트 통과 |
|
||||
| M7 | `?T`, `E!T`, try/catch | io 유닛 동작 |
|
||||
| M8 | 유닛/import/.fei, 분리 컴파일, std 초안 | 다중 유닛 프로그램 빌드 |
|
||||
| M9 | **제네릭** (모노모피제이션) | `List(T)`, `Map(K,V)`를 Ferro로 재작성 |
|
||||
| M10 | bits16 타깃: far, `@seg_ptr`, 메모리 모델, asm, interrupt fn | DOSBox에서 VGA 데모 실행 |
|
||||
| M10 | bits16 타깃: far, `@seg_ptr`, 메모리 모델, asm, interrupt fn, `shared`/`atomic`/`critical`/`interrupt_safe` | QEMU FreeDOS에서 자동화된 far 포인터·인터럽트 공유 상태 테스트 통과(VGA 데모는 수동/멀티모달 검증 대상이라 완료 게이트에서 제외) |
|
||||
| M11 | 컴파일러 B를 Ferro로 작성, A로 빌드 | B가 M1~M10 테스트 통과 |
|
||||
| M12 | 셀프호스팅 fixpoint | B(B(B)) == B(B) 바이트 동일, A 폐기 |
|
||||
| M13 | 386 네이티브 백엔드 | gcc 없이 빌드, 컴파일 속도 10배 |
|
||||
@@ -644,13 +690,15 @@ Uninit | Owned | Moved | MaybeMoved | Shared(n) | Exclusive
|
||||
tests/
|
||||
pass/*.fe + *.expected 컴파일→실행→stdout 비교
|
||||
fail/*.fe 첫 줄 "// ERROR:<line>:<메시지 일부>"
|
||||
run16/*.fe bits16 빌드 후 DOSBox 실행, 출력 파일 비교
|
||||
run16/*.fe bits16 빌드 후 QEMU FreeDOS 실행, 출력 파일 비교
|
||||
boot/ A/B 출력 비교, fixpoint 검증
|
||||
```
|
||||
|
||||
- `fail/`은 규칙별 최소 3개: R1(이동 후 사용), R3(직접 drop 호출), R4(필드에 참조), R5(스코프 초과), R6(배타성 위반), R7(무효화), R8(참조 반환 바인딩), R9(unsafe 밖 raw 역참조), match 완전성, 암묵 변환, 타입 불일치.
|
||||
- 포매팅(§6.3.1) 전용 `fail/` 케이스: `{}` 개수 > 인자 개수, 인자 개수 > `{}` 개수, 미지원 verb(`{q}`), 런타임 값 포맷 문자열, 대응 `write_*` 없는 타입(예: struct), 닫히지 않은 `{`.
|
||||
- 포매팅 `pass/` 케이스: 각 verb 1개 이상, `{{` 이스케이프, 인자 0개, `@sprint` 반환 길이 검증, `@fprint`를 `io.buf_writer`로 호출.
|
||||
- 포매팅(§6.3.1) 전용 `fail/` 케이스: `{}` 개수 > 인자 개수, 인자 개수 > `{}` 개수, 미지원 verb(`{q}`), 런타임 값 포맷 문자열, 대응 `write_*` 없는 타입(예: struct), 닫히지 않은 `{`, `try @print(...)`(void).
|
||||
- 포매팅 `pass/` 케이스: 각 verb 1개 이상, `{{` 이스케이프, 인자 0개, `@print`의 오류 삼킴, `@sprint` 반환 길이/잘림 검증, `@fprint`를 `io.buf_writer`로 호출.
|
||||
- `@compile_error`, `@as_far_fn`, `@call_far`의 comptime/타깃/unsafe 제약과 `error.Name`의
|
||||
`core.Error` 등록, 결정적 코드, `--strip-error-names`, `fmt.write_error`를 각각 pass/fail로 검증한다.
|
||||
- 각 마일스톤은 해당 기능의 pass/fail 테스트와 함께 완료한다.
|
||||
- 회귀 실행: `make test` — 전 타깃 전 테스트.
|
||||
|
||||
@@ -673,8 +721,9 @@ tests/
|
||||
| 레이블 있는 break | 편의 | — | 플래그 변수 |
|
||||
| 슬라이스 패턴 매칭 | 편의 | — | 인덱스 비교 |
|
||||
| `inline fn` | 편의 | — | C 방출 시 `static inline` |
|
||||
| `must` 키워드 | 편의 | 실패를 트랩으로 바꾸는 문법 설탕일 뿐 핵심 의미론이 아님 | `expr catch @trap()` |
|
||||
| 라이프타임 표기 (`'a`) | **구조적 불가** | 전역 분석 필요, R4를 풀어야 함 | R4 (2급 참조), 인덱스 핸들 |
|
||||
| 스레드 | 구조적 불가 | DOS에 없음 | — |
|
||||
| 선점형 스레드 | 구조적 불가 | DOS 기본 실행 모델에 없고 함수 단위 소유권 모델을 넘어서는 동기화가 필요 | — |
|
||||
| 매크로 / 전처리기 | **영구** | 도구 지원과 컴파일 속도 파괴 | `const`, `comptime if`, 제네릭, `@print` |
|
||||
| 예외 | 영구 | 언와인딩 기반 시설 없음, 숨은 비용 | 에러 유니온 |
|
||||
| GC | 영구 | 결정적 비용 원칙 위반 | 소유권 + RAII + 아레나 |
|
||||
|
||||
Reference in New Issue
Block a user