Skip to content

3. 실행기: 코드 실행의 프로젝트 매니저

지금까지 우리는 코드를 잘게 쪼개고(토크나이저), 구조적인 나무로 엮었습니다(파서). 이제 여정의 마지막 단계, 바로 **실행기(Executer)**입니다.

실행기의 역할

  1. 실행 컨텍스트(Scope) 관리: 코드가 실행되는 동안 변수와 함수가 어디에 저장되고, 어떻게 찾아지는지를 결정하는 Scope를 생성하고 관리합니다. 이는 코드의 생명 주기와 유효 범위를 결정하는 가장 중요한 요소입니다.
  2. AST 노드 실행 위임: 실행기 자체가 AST 노드를 순회하며 모든 로직을 처리하는 것이 아닙니다. 대신, 각 AST 노드(Executable 인터페이스를 구현한)에게 자신의 execute 메소드를 호출하여 실제 실행 로직을 위임합니다. 즉, 각 노드가 스스로를 어떻게 실행할지 알고 있는 구조입니다.
  3. 제어 흐름 시그널 처리: 함수 반환(return), 반복문 탈출(break)과 같이 일반적인 코드 흐름을 벗어나는 특별한 상황(Signal)을 감지하고, 이를 적절한 에러로 변환하거나 상위 호출자에게 전달하여 프로그램의 제어 흐름을 관리합니다.

executer 함수 들여다보기

'달빛 약속'의 실행 로직은 주로 /core/executer/index.ts 파일의 executer 함수에서 시작됩니다. 이 함수는 다음과 같은 중요한 일을 합니다.

typescript
export async function executer<NodeType extends Executable>(
    node: NodeType,
    codeFile?: CodeFile,
): Promise<Scope> {
    const scope =
        codeFile?.ranScope ||
        new Scope({
            codeFile,
        })

    try {
        await node.execute(scope) // 핵심: 노드에게 실행을 위임
        return scope
    } catch (e) {
        if (e instanceof ReturnSignal) {
            throw new CannotReturnOutsideFunctionError({
                tokens: e.tokens,
            })
        }

        if (e instanceof BreakSignal) {
            throw new BreakNotInLoopError({
                tokens: e.tokens,
            })
        }

        throw e
    }
}

위 코드에서 볼 수 있듯이, executer 함수는 특정 nodeexecute 메소드를 호출하는 것이 핵심입니다. 그리고 이 execute 메소드가 실행되는 동안 발생하는 ReturnSignal이나 BreakSignal 같은 특별한 시그널(예외)을 캐치하여 적절한 에러로 변환합니다.

Scope: 실행 컨텍스트의 핵심

실행기의 가장 중요한 파트너는 바로 /core/executer/scope.ts에 정의된 Scope 클래스입니다. Scope는 변수와 함수를 저장하고 관리하는 공간이며, 다음과 같은 특징을 가집니다.

  • 계층적 구조: 각 Scope는 부모 Scope를 가질 수 있으며, 이를 통해 **스코프 체인(Scope Chain)**을 형성합니다. 변수나 함수를 찾을 때 현재 스코프에 없으면 부모 스코프로 거슬러 올라가며 재귀적으로 탐색합니다. 이는 '달빛 약속' 언어의 클로저와 변수 유효 범위를 결정하는 핵심 메커니즘입니다.
  • 변수 및 함수 관리: setVariable, getVariable, addFunctionObject, getFunctionObject와 같은 메소드를 통해 변수와 함수를 안전하게 관리합니다.
  • CodeFileSession 연결: Scope는 자신이 속한 CodeFileSession에 대한 참조를 가질 수 있어, 실행 중인 코드의 위치와 전반적인 실행 환경에 대한 정보를 제공합니다.

Signals: 특별한 제어 흐름

/core/executer/signals.ts에 정의된 Signal 클래스들은 '달빛 약속'의 특별한 제어 흐름을 처리하기 위한 메커니즘입니다. 예를 들어, 함수 내에서 돌려주기 (return) 명령을 만나면 ReturnSignal이 발생하고, 반복문 내에서 탈출 (break) 명령을 만나면 BreakSignal이 발생합니다. 이 시그널들은 JavaScript/TypeScript의 throw 문을 통해 예외(Exception)처럼 발생하며, executer 함수나 상위 호출자에서 try...catch 블록을 통해 이를 처리합니다. 이를 통해 일반적인 코드 흐름을 벗어나 프로그램의 흐름을 변경합니다.