렉서 다음은 파서다. 노드는 한 배열에 살고 자식은 그 안의 인덱스다 -- 노드는
^Node 를 들 수 없고(여럿이며 한 번씩 소유하지 않는다) &Node 도 들 수 없다(R4).
인덱스는 둘 다 아니다. 소스도 필드가 아니라 매 단계에 같이 다닌다.
unit demo / fn answer @2 / let n = (+ 1 (* 2 3)) / return n / balanced
전위 표기로 다시 찍는 것이 시험의 요점이다. 1 + 2 * 3 이 어떻게 묶였는지는
그렇게만 보인다.
쓰면서 나온 컴파일러 버그 넷:
1. 다른 유닛의 타입을 필드로 쓰면 그 필드 타입이 영영 UNKNOWN 이었다. 필드
해석이 유닛마다 선언 직후에 돌아서, 아직 선언되지 않은 유닛의 타입을 찾다
실패하고 그 답을 굳혔다. 이제 모든 유닛이 선언을 마친 뒤에 한 번 푼다.
2. 그리고 그 해석은 타입을 선언한 유닛에서 해야 한다. 필드 타입은 그 유닛의
import 로 쓰였는데 아무 유닛에서나 풀고 있었다. 타입 계층에 enter/leave
콜백을 두고 체커가 그 자리로 데려간다.
3. cycle_state 를 재귀 검사와 크기 계산이 같이 썼다. 첫 번째가 보는 중인 구조체
가 두 번째에게는 다 끝난 것으로 보여서, 필드가 하나뿐인 것처럼 1 바이트로
자리를 잡았다 -- Parser 가 그래서 자기 토큰을 밟았다. layout_state 로 나눴다.
4. 다른 유닛의 상수(ast.NONE)를 lowering 이 필드 접근으로 봤다. 체커가 이미
링크 이름을 붙여두었으니 그것이 있으면 전역이다.
그리고 R1 을 실제로 지키게 했다: 소유자를 놓으면 그것이 가진 것도 놓는다.
전에는 자기 drop 이 있거나 자기가 owned 일 때만이어서, drop 을 가진 타입을
필드로 담은 구조체는 그것을 놓을 방법이 없었다(drop 은 손으로 못 부른다).
이제 release_at 이 drop 을 부르고 필드로 내려간다. 그 덕에 List/Arena/Map 의
drop 이 전부 필요 없어져서 지웠다 -- 버퍼가 owned 이니 R1 이 알아서 한다.
221/221, 29/29.
컴파일러는 이름을 끊임없이 찾는데 리스트 선형 탐색은 그 모양이 아니다. 키는
맵이 소유하는 한 버퍼에 복사되고 슬롯은 그 안의 어디인지만 적는다 -- R11 이
말하는 아레나와 핸들 모양이고, 그래서 맵을 놓는 것이 엔트리마다 하나가 아니라
두 번의 해제다.
개방 주소법에 선형 탐사. 표는 2의 거듭제곱이라 나눗셈이 아니라 마스크이고,
탐사가 길어지는 것이 표가 차는 것보다 먼저라 3/4 에서 자란다.
길에서 고친 것 넷:
- 인스턴스를 만들 때 선언 유닛으로 전환하지 않아서, 필드 타입 Slot(V) 를
호출자 유닛에서 찾고 있었다.
- mem.alloc_slice 가 원소 타입으로 단순한 이름만 받았다. Slot(V) 같은
인스턴스도 받는다.
- 쓸 수 있는지를 바인딩이 아니라 소유된 것이 정한다. let p: ^[]mut T 는 p 를
고정하고 그것이 소유한 것은 쓸 수 있게 둔다. 슬라이스 인덱스도 마찬가지다.
- 메서드 인자에 자유 함수와 같은 호출 한정 약화가 없었다.
알게 된 것: 대여는 루트 단위라 self 의 한 필드에 쓰는 동안 다른 필드를 읽을 수
없다. 지역으로 빼거나 메서드를 나누면 되지만, 필드 단위 대여가 있으면 훨씬
편할 자리다.
count 5 / fn 2 let 3 missing 0 / grown 64 / after 40 / balanced
218/218, 28/28.