ANTLR를 이용한 JASMIN 컴파일러

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

Beige00 2024. 1. 10. 13:25

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

 

- Test.tpy 컴파일링 과정 따라가보기

def sum(a, b):
return a + b

a = 3
b = 4

c=sum(a,b)
print(c)

if a>=3:
print(a)
elif b<2:
print(b)
elif b>3:
print(3)
else:
print(c)

while c<6+10:
if c==7:
print "c=7"
c=c+1
elif c==9:
print "break point"
break
else:
print(c)
c=c+1

print "Hello world! This is final Term project."

 

사전에 업로드한 Test.tpy 파일을 기반으로 작성한 컴파일러가 어떻게 작동할지 따라가보자.

defs tree

사전에 정의한대로, 사용할 함수 선언은 전부 소스코드의 최상단에 기록하는 것을 규칙으로 했었다.

따라서 program -> file_input -> defs 순으로 먼저 파싱이 될 것이다.

program이 파싱될 때, enterProgram이 실행될 것이다.

따라서 .class public Test ~ .end method까지 결과 파일에 기록되게 된다.

그 후 실행된 file_input에는 별다른 enter, exit method를 정의해주지 않았으므로 바로 enterDefs가 실행된다.

enterDefs는 파라미터로 입력된 ctx의 하위 트리들을 좌측부터 순서대로 스캔하며 def stmt일시 enterDef를 해주는 method이다.

본 소스 코드의 defs tree에는 def_stmt가 하나밖에 없으므로 enterDef를 한번 실행 해줄 것이다.

 

defs에서 실행한 enterDef는 빨간 동그라미 부분 subtree를 가지고 있다.

작성한 enterDef를 보면 우선 .method public static "2번째 child(sum)" ( 까지를 기록해준다.

그 다음 4번째 child를 enterArgs의 파라미터로 주며 enterArgs를 실행한다.

 

실행된 enterArgs는 사전에 정의한대로 자신이 가지고 있는 모든 NAME 속성의 expr마다 I 를 작성해준다.

Test.tpy에서는 a,b이므로 II 가 기록될 것이다.

그 후, 자신을 call한 enterDef로 return된다.

 

다시 enterDef이다.

enterArgs가 콜백되었을 때 기준으로 result에는 .method public static sum(II 까지가 기록되게 된다.

(즉, sub tree 상 4번째 child, 'args'까지 처리가 되었다는 의미.)

따라서 CLOSE_PALEN에 해당하는 ')'를 result에 기록해준다. 6번째 child인 ':'는 따로 처리를 해주지 않았으므로 생략된다.

그 뒤에는 무조건 return되는 자료형은 가정에 의해 int가 될 것이므로 I를 따로 기록해준다.

이 시점에서 .method public static sum(II)I 가 result string에 기록되게 되며, 그 후 .limit stack 32~ 를 기록해주어 함수의 헤더를 정의 완료한다.

그 다음은 함수의 body이다. 이는 g4 rule의 정의 상, def_stmt tree의 마지막 node "suite"가 가지고 있을 것이다.

아직 처리해주지 않은 마지막 child suite를 처리해주기 위해 enterSuite(ctx.getChild(6))를 해준다.

 

실행된 enterSuite는 입력된 ctx가 'simple_stmt' 인지, NEWLINE stmt+인 지를 따져 적절한 enter 함수를 실행해주는 역할이다. 

따라서 NEWLINE stmt:1을 가지고 있는 ctx는 가지고 있는 stmt:1을 enterStmt 할 것이다.

 

enterStmt는 다시 입력받은 ctx가 simple_stmt인지 Compound_stmt인지를 따져 enter해준다.

본 소스코드에서는 simple_stmt이므로 enterSimple_stmt가 실행될 것이다.

 

enterSimple_stmt에서는 무조건 small_stmt NEWLINE 이므로 enterSmall_stmt를 실행한다.

 

enterSmall_stmt에서는 입력받은 simple_stmt의 종류를 파악한다.

본 소스코드에서는 return_stmt였다. * (return a+b)이기 때문에

 

enterReturn_stmt에서는 return expr 을 처리해준다.

우선 return이 될 expr을 enter해준다.

 

enterReturn에 의해 call된 enterExpr은 현재 a+b를 들고 실행됐다.

따라서 enterExpr 함수의 else문에 걸리게 되고, 정의에 따라 enterExpr(a), enterExpr(b), result에 iadd 작성을 실행할 것이다.

 

각 enterExpr(a), enterExpr(b)는 ctx.NAME()!=null 조건에 걸린다.

a,b는 현 함수 정의 시점에서는 enterArgs()에 의해 table에 추가된 변수들이다. (enterArgs 코드 참조)

따라서 다음의 처리가 되게 된다.

if(table.contains(ctx.getChild(0).getText())) {
    this.result+=("iload_"+table.indexOf(ctx.getText())+"\n");
}//변수 호출

 

결국, iload_"table의 'a', 'b' index에 해당하는 number" 가 result에 기록되게 된다.

(iload_0, iload_1)

여기까지 기록이 된 후 enter 함수들이 차례대로 return되며 enterSuite까지 종료되게 된다.

 

길고도 긴 과정을 지나 enterDef로 돌아왔다. Suite까지 처리가 되면 함수가 종료된 것이므로 .end method를 result에 기록해주고, 함수 선언 동안 사용한 임시 변수 저장 메모리 "table"을 초기화해준다. 또한 임시 함수 이름 저장 메모리 tempFuncFormat 또한 초기화 해주며 enterDef을 끝낸다.

 

enterDef가 끝나면 enterDefs에서는 다음 def_stmt를 처리해주려고 한다. 그러나 sub_tree가 끝나버렸으므로 enterDefs가 종료되며 exitDefs가 실행된다. exitDefs가 실행되면 이제 함수의 선언이 끝나고 앞으로는 main 함수의 선언만이 남았으므로 다음과 같이 메인 함수의 헤더를 작성해주며 종료된다.

this.result+=(".method public static main([Ljava/lang/String;)V\n");
this.result+=(".limit stack 32\n");
this.result+=(".limit locals 32\n");

 

더보기

* 해당 과정까지 끝난 시점에서 result의 내용. 

.class public Test
.super java/lang/Object
; standard initializer
.method public <init>()V
aload_0
invokenonvirtual java/lang/Object/<init>()V
return
.end method
.method public static sum(II)I
.limit stack 32
.limit locals 32
iload_0
iload_1
iadd
ireturn
.end method
.method public static main([Ljava/lang/String;)V
.limit stack 32
.limit locals 32

 

Test class의 선언과 사용할 함수들의 정의, main 함수의 시작부분이 기록되었음을 알 수 있다.


이런 식으로 parse tree 관찰 => enter 함수 관찰 => 재귀 과정 따라가기 => exit 함수 관찰 => 다음 parse tree 관찰

과정을 따르면 어렵지 않게 뇌로도 컴파일러를 따라가볼 수 있을 것이다.

 

2. 결과 출력법

- ANTLR 플러그인을 설치해준다.

(File - Settings - Plugins)

 

- Jasmin을 다운로드한다.

(http://jasmin.sourceforge.net/)

 

- Jasmin 사용법.

     ( jasmin.jar를 해당 디렉토리가 가지고 있어야한다. )

 

- Parse tree

ANTLR plugin을 설치했다면 해당 탭에서 g4 rule을 선택해주고 가지고 있는 tpy 파일을 입력해주면 Parse tree가 생성된다.

 


이 글을 끝으로 이번 학기에 했던 내용은 전부 끝났다.

시간이 있다면 글을 설명하며 찾아낸 사칙연산 부분과 다양한 자료형 파싱 등을 추가 구현 해볼 것이다.