새로운 Smart Contract 프로그래밍 언어 만들기 — Compiler (1)

hihiboss
DE-labtory
Published in
7 min readJan 23, 2019

안녕하세요. koa팀 개발자 김준희입니다. 이번 글에서 koa 프로젝트의 compiler 컴포넌트에 대해 간단하게 설명하고자 합니다.

koa 프로젝트?

저희 프로젝트는 크게 koa와 koa-playground로 나눌 수 있습니다. koa는 프로그래밍 언어 그 자체이고, koa-playground는 koa 언어로 프로그램을 만들 수 있는 웹 IDE입니다.

koa architecture
  • Lexer: 소스 코드를 받아서 token들로 바꿉니다.
  • Parser: token들을 받아서 AST(Abstract Syntax Tree)로 바꿉니다.
  • Compiler: AST를 이용해서 컴퓨터가 이해할 수 있는 ByteCode로 바꿉니다.
  • VM: Bytecode를 직접 실행시켜 결과물을 얻습니다.

Compiler?

프로그래밍 처음 배우실 때 어떻게 배우셨나요?

#include <stdio.h>int main(void) {
printf("Hello, world!\n");
return 0;
}

헬로월드! 저는 이 코드를 먼저 실행시켰던 기억이 납니다. 코드가 뭘 의미하는지, 내가 누른 버튼들이 무슨 버튼인지도 모른 채 ‘Hello, world!’가 출력된 콘솔 창을 보며 신기해했습니다. 그렇다면 이 코드를 만들고 실행시키는 과정은 어떻게 진행되는 것일까요?

우리는 이전 글에서 lexer와 parser에 대해서 알아보았습니다. lexer, parser가 소스 코드를 읽어 의미 단위로 나누고 문법 검사까지 완료했다면, 이제는 실제로 컴퓨터가 실행할 수 있도록 코드를 바꾸어야겠죠? 앞에서 열심히 만들어준 AST(Abstract Syntax Tree)를 컴퓨터가 이해할 수 있는 언어로 바꾸는 것! 이것이 바로 compiler의 역할입니다.

A compiler is a computer program that transforms computer code written in one programming language (the source language) into another programming language (the target language). Compilers are a type of translator that support digital devices, primarily computers. The name compiler is primarily used for programs that translate source code from a high-level programming language to a lower level language (e.g., assembly language, object code, or machine code) to create an executable program

위키피디아에서는 위와 같이 설명하고 있습니다. 요약하자면, 어떤 프로그래밍 언어로 작성된 코드를 다른 프로그래밍 언어로 바꾸어주는 프로그램이 compiler라는 것입니다. 우리가 C나 Java로 프로그래밍을 할 때, 컴파일 버튼을 눌러 생성되는 class 파일 혹은 java 파일이 바로 컴퓨터가 알아들을 수 있게 바뀐 것입니다. 이제 compiler가 뭐하는 놈인지 대충 아시겠죠? 하지만 ‘이게 어떻게 바뀌는거지?’ 라고 궁금하실 겁니다. 예시를 하나 볼까요?

1 + 2

위와 같은 코드를 컴파일한다고 가정합시다. 컴퓨터가 실행할 수 있도록 어셈블리어로 바꾸어 봅시다. 먼저 덧셈 연산자는 2개의 피연산자를 필요로 합니다. 따라서 스택에 2개의 피연산자를 먼저 넣은 뒤, 덧셈 연산자를 넣어 실행하도록 만들면 됩니다!

PUSH 1 PUSH 2 ADD

1 + 2 라는 코드를 어셈블리어로 바꿔보았습니다. 처음으로 오는 피연산자 1 을 스택에 넣고, 뒤에 오는 피연산자 2를 스택에 넣은 뒤, ADD라는 연산자를 넣어주는 것입니다. 컴퓨터는 PUSH, ADD 와 같은 명령어는 미리 알고 있기 때문에 위 코드를 실행할 수 있습니다.

잠깐! 컴퓨터는 어셈블리어를 미리 알고 있다구요?

컴퓨터는 0과 1 (영과 일… 영과일… 영 과일… Zero Fruit… zeroFruit?!)로 동작합니다. 하지만 어셈블리어는 그렇지 않으므로 미리 어셈블리어 명령어를 2진수로 바꾸어 컴퓨터가 미리 알고 있도록 하는 것입니다. (하지만 2진수는 보기 너무 힘들기 때문에 편의상 16진수로 표현합니다.) 예를 들어 PUSH 명령어는 60, ADD 명령어는 01 이라는 16진수로 미리 정해두는 것입니다. 실제로 이더리움의 solidity 언어는 어셈블리어와 비슷한 opcode로 명령어들을 정의하고 있습니다. (여기에서 실제 이더리움의 opcode를 확인할 수 있습니다.)

그럼 PUSH 1 PUSH 2 ADD를 바꿔볼까요? PUSH60, 120102, ADD01로 바뀌니까 60 01 60 02 01과 같은 결과가 나오겠네요. 이와 같은 16진수 코드를 bytecode라고 부릅니다.

KOA Compiler?

const (

// Pop the first two items in the stack.
// Add popped two items and push to the stack.
//
// Ex)
// [a]
// [b] ==> [a+b]
// [x] [x]
//
Add Type = 0x01

// Pop the first two items in the stack.
// Multiply popped two items and push to the stack.
//
// Ex)
// [a]
// [b] ==> [a*b]
// [x] [x]
//
Mul Type = 0x02

...

// Pop the first item in the stack.
//
// Ex)
// [a]
// [b] ==> [b]
// [x] [x]
//
Pop Type = 0x20

// Push the 32bits(uint32) item .
//
// Ex)
// [a]
// [b] ==> [b]
// [x] [x]
//
Push Type = 0x21
)

koa가 어떤 명령어를 수행할지는 위와 같은 opcode로 정의됩니다. 그리고 compiler는 프로그래머가 작성한 코드를 정의된 opcode로 바꾸는 역할을 합니다. 즉, koa 코드의 연산, 변수에 값 할당, if 조건문, function 정의 등을 모두 opcode로 바꾸어 16진수의 bytecode로 만들어내는 것이 compiler의 역할입니다.

지금까지 compiler를 알아보았습니다. compiler는 어떤 프로그래밍 언어로 작성된 코드를 다른 프로그래밍 언어로 바꾸는 일을 합니다. 그리고 koa 프로젝트의 compiler는 koa언어로 작성한 코드를 opcode로 구성된 bytecode로 바꾸는 일을 합니다. 이렇게 만들어진 bytecode를 vm에게 전달하여 실행하면 결국 프로그램이 실행되는 것입니다. 어렵지만 알면 알수록 매력적인 compiler입니다!

--

--