클래스와 상속
Source: Dev.to
우리는 이제 클래스 지원을 추가하여 인터프리터를 완성합니다.
클래스는 다른 클래스로부터 상속받아(속성과 메소드를 재사용하면서) 동시에 수정 및 추가를 통해 맞춤화를 허용합니다.

제가 만든 것
커밋 bac3f3c, bfa82b9, 03b3af3, 6c70087
Source:
내가 이해한 내용
-
클래스 선언
- 파서는 각 클래스 선언에 대해
Stmt.Class노드를 생성하고, 여기에는 클래스 이름과 메서드 목록이 들어갑니다. - 리졸버는 현재 스코프에 클래스 이름을 선언하여 클래스가 자기 자신을 참조할 수 있게 합니다(예: 재귀 호출).
- 인터프리터가
Stmt.Class를 방문하면, AST 노드를LoxClass객체로 변환하고, 이 객체는 메서드들을LoxFunction으로 변환한 맵을 저장합니다.
- 파서는 각 클래스 선언에 대해
-
클래스 인스턴스 / 객체
- Lox는
new키워드를 사용하지 않습니다. LoxClass는LoxCallable인터페이스를 구현하므로, 클래스를 호출할 때마다 인스턴스가 생성됩니다.- 클래스를 호출하면 새로운
LoxInstance가 만들어지고 반환됩니다. - 인스턴스는 자신의 클래스(
LoxClass)에 대한 참조를 유지하여 이후에 메서드를 찾을 수 있습니다.
- Lox는
-
프로퍼티
- 필드는 클래스에 선언되지 않으며, 인스턴스를 통해 할당될 때 생성됩니다.
- 인터프리터가
LoxInstance인 객체를 평가할 때 먼저get()을 호출하여 인스턴스의 필드 맵을 확인합니다. - 필드가 존재하면 그 값을 반환하고, 없으면 메서드를 찾습니다. 두 경우 모두 찾지 못하면 런타임 오류가 발생합니다.
- 파서는
=기호를 보기 전까지는 set 표현식과 get 표현식을 구분할 수 없습니다. 따라서 왼쪽 피연산자를 일반 표현식(get)으로 파싱하고,=를 만나면 해당 노드를set노드로 변환합니다. set()이 호출되면fields맵이 업데이트되며(기존 키가 있으면 덮어쓰기) 됩니다.
-
메서드
- Lox 메서드는
fun키워드가 없으며 인스턴스를 통해 접근합니다. - 메서드를 추출해서(예: 변수에 할당해서) 나중에 호출하더라도
this는 메서드를 추출한 인스턴스를 가리켜야 합니다. - 이를 위해 메서드에 접근할 때
LoxClass는 기본LoxFunction에bind(instance)를 호출합니다. bind()는 메서드의 원래 클로저 안에 중첩된 새로운 환경을 만들고, 그 환경에this를 정의한 뒤 새로운LoxFunction을 반환합니다.this가 클래스 밖에서 사용되면, 현재 클래스 내부인지 여부를 추적하는 리졸버가 오류를 보고합니다; 그렇지 않으면this는 일반 로컬 변수처럼 해결됩니다.- 인터프리터는 다른 변수와 마찬가지로 환경에서
this를 찾아내며, 바인딩 단계 덕분에 올바른 인스턴스를 얻습니다.
- Lox 메서드는

-
생성자
- 사용자가 정의한 생성자는
init이라고 부릅니다. LoxClass.call이init메서드를 찾으면, 새로운 인스턴스를 설정하기 위해 즉시 호출합니다.- 호출 시 인자의 개수(arity)는 초기화 메서드의 매개변수 개수와 비교됩니다.
init은 항상this를 반환합니다(사용자가 다른 값을 반환하려고 해도). 리졸버는LoxFunction에isInitialiser플래그를 두어 이를 강제합니다.
- 사용자가 정의한 생성자는
-
상속
- 서브클래스는 슈퍼클래스에 저장된 참조를 통해 메서드를 상속받을 수 있으며, 메서드 탐색은 상속 체인을 따라 올라갑니다.
- 파서는 이제
Stmt.Class노드에superclass필드(Expr.Variable)도 포함시킵니다. - 리졸버는 자기 자신을 상속하는 경우를 검사하고, 발견되면 오류를 보고합니다.
- 슈퍼클래스가 존재하면, 리졸버는 새로운 스코프를 만들고 그 안에
super를 정의하여 서브클래스의 모든 메서드가super를 사용할 수 있게 합니다. - 서브클래스를 만들 때 인터프리터는 새로운 환경을 생성하고,
super를 슈퍼클래스를 가리키도록 정의한 뒤 메서드들을 생성합니다. - 메서드 탐색은 먼저 인스턴스의 필드를 확인하고, 그 다음 클래스의 메서드, 마지막으로 슈퍼클래스의 메서드(존재한다면) 순으로 진행됩니다. 체인의 끝이
null이면 탐색에 실패합니다.
auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fucafxqk8gaix3iou4h7h.png)
supersuper키워드는 서브클래스가 메서드를 오버라이드하면서도 부모 클래스의 원래 구현을 호출할 수 있게 해줍니다.- 같은 메서드를 연속된 서브클래스가 오버라이드하더라도,
super는 항상 올바른 바로 위의 부모 구현을 가리킵니다.
위 내용은 상속, 생성자, 그리고 super 키워드를 포함한 전체 클래스 지원을 추가한 후 Jlox 인터프리터의 현재 상태를 반영합니다.
클래스 정의
- 점(
.)과 식별자(super.method)가 뒤따르며Expr.Super노드로 파싱됩니다. - 슈퍼클래스의 메서드를 찾아 현재 인스턴스(
this)에 바인딩하지만, 두 객체는 서로 다른 환경에 속합니다. - 리졸버는
super를 로컬 변수로 간주하고super가 정의된 환경으로부터의 거리(홉 수)를 계산합니다.
다음 내용:
Lox에 대한 몇 가지 확장—리스트 지원 및 for‑in 루프.
사색
이 프로젝트에 대해 블로그에 기록하는 것이 저에게 정말 큰 도움이 되었습니다. 현재 다른 프로젝트들을 진행하고 있지만, (몇 달 전!) 제가 했던 일을 설명하려고 하면 그 세부 사항과 그 뒤에 있는 이유를 떠올리게 됩니다. 어느 정도는, 이전에 얻은 통찰이 현재 작업을 접근하는 방식에 계속 영향을 미칩니다.
나는 내가 하는 일을 기록하는 것을 정말 좋아합니다, 비록 그것이 가장 일상적인 활동이라 할지라도. 인도 철학에서는 일이 가장 높은 헌신 행위 중 하나라고 합니다. 우리가 무엇을 하든, 의도를 가지고 하면 그것이 우리를 능숙함과 숙련도로 이끌며, 배우고 실천하는 과정이 우리를 최고의 모습으로 만들기 때문입니다. 타고르가 잘 말했듯이:
“지칠 줄 모르는 노력의 팔이 완벽을 향해 뻗을 때…”
그 완벽함 안에 신이 있지 않을 곳이 어디겠습니까? 그래서 나는 내가 배운 모든 것을 스스로에게 상기시킵니다.
