ANTLR를 이용한 JASMIN 컴파일러

ANTLR를 이용한 tinyPython to Jasmin 컴파일러 만들기. - 1

Beige00 2023. 12. 27. 12:56

* 본 내용은 충남대학교 조은선 교수님의 컴파일러 개론을 수강하고 작성한 글입니다.

 

이번 학기에 텀프로젝트로 수행한 tinyPython to Jasmin 컴파일러가 나름 재미있게 만든 경험이라고 생각해 정리해보기로 했다.

 

우선, 전체적인 텀프로젝트 수행 제약 조건은 다음과 같다.

 

1. Class 정의는 기본적으로 전재한다고 가정
2. 모든 함수는 static 메소드로 가정
3. 함수 정의는 맨 위에서 순서대로 나타남. 그 아래에 main 함수에 해당하는 구문들이 작성 됨
4. 함수 안의 함수 등의 nesting은 없다고 가정.
5. Java Bytecode로 변환 시 Main 함수는 반드시 존재. 생성해줘야함
6. 함수의 인자와 리턴 타입은 int 타입만
7. 사칙 연산 비교 연산은 int만 허용

 

또한 추가적으로 컴파일러가 사용할 결과 및 텍스트를 담을 문자열이 필요, 컴파일러가 사용할 식별자에 대한 심볼테이블 이 필요하다.

 

과제 난이도의 완화를 위해 모든 함수는 public static으로 구성하고, int 형 이외의 자료형은 반환하거나 파라미터로 사용하지 않는다고 가정한다. (Print 제외)

-> 이 조건은 본 텀프로젝트를 정리하면서 제거해볼 생각임.

 

배열과 예외 또한 없다고 가정한다.

 

컴파일링에 사용할 파일은 첨부한 Test.tpy 파일이다.

(테스트에 사용할 g4 파일은 내가 직접 작성한 것이 아니라 첨부하지 못했다.)

 

이번 포스팅에서는 어떤 식으로 내가 짠 컴파일러가 구성되고, 실행되는지 설명을 하겠다.

import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.ParseTree;


public class Main {

    public static void main(String[] args) throws Exception {
        tinyPythonLexer lexer = new tinyPythonLexer(CharStreams.fromFileName("Test.tpy"));
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        tinyPythonParser parser = new tinyPythonParser(tokens);
        ParseTree tree = parser.program();

        ParseTreeWalker walker = new ParseTreeWalker();
        walker.walk(new MyComp(), tree);

    }
}

우선 Test.tpy 파일을 열고, 해당 파일을 읽어가며 매칭되는 표현에 짜여있는 SDD(g4 파일) rule에 따라 액션을 취하는 구조이다.

파싱은 walker를 이용하였다.

알아보기 어렵겠지만, 해당 Test.tpy를 파스 트리로 나타낸 것이다.
조금 더 확대해서 일부분만 나타낸 사진이다.

위의 사진을 기반으로 어떻게 컴파일러가 동작할 지 예상을 하면 편하다. 우선 defs라는 컨텍스트에 들어가면, 

g4 파일 상에 (NEWLINE | def_stmt)* 라고 정의가 되어있다. 여기서 *은 정규 표현식과 같이 0번~n번 반복될 수 있음을 의미한다.

def_stmt는 'def' NAME OPT_PAREN args CLOSE_PAREN ':' suite 로 정의되어있는데

흔히 우리가 파이썬에서 볼 수 있는 def A(a,b): return a 와 같은 구조를 의미한다.

이를 기반으로 위의 사진을 다시 보면, defs에서 'def_stmt NEWLINE' 가 파싱되었고, 파싱된 def_stmt에서는 SDD에 정의된 대로 'def' NAME OPT_PAREN args CLOSE_PAREN ':' suite 를 찾아 매칭시키는 것을 볼 수 있다.

그렇다면 우리는 해당 노드를 방문했을 때 어떠한 작업을 컴파일러가 해줄지를 정의해주면 되겠다.

 

즉, def_stmt를 마주했을 때, 우리의 컴파일러가 어떠한 행동을 할지를 정해주는 것이 이번 텀프로젝트의 메인 아이디어가 되겠다.

 

ex) defs가 매칭되면 g4 rule을 보니까 def_stmt거나 NEWLINE이군, 그럼 내 컴파일러는 def_stmt나 NEWLINE을 찾아야겠어. 만일, def_stmt나 NEWLINE이 없으면 g4 rule에 위배되는 패턴이니 오류처리를 해주어야겠어.

 

여기까지가 텀프로젝트를 수행하기 위한 메인 아이디어다. 여기까지 이해가 되었으면 다음은 기계적으로 g4 룰에 따라 defs에 들어가면 def_stmt를 찾고, def_stmt에 들어가면 def, NAME... 을 찾고... 를 반복하면 된다.

그 반복이 끝날 때는 항상 terminal에 마주하게 될텐데, terminal에서는 단순히 이어지는 노드를 찾는 것이 아닌, 어떠한 액션을 취해주어야 한다.

 

다음 내용부터는 세부적인 구현을 포스팅 할 것이다.

Test.tpy
0.00MB