이론없이 컴파일러 만들기 꿈파일러 5

16 minute read

자료형 정의

먼저 Token 을 정의한다. str 은 토큰의 내용이고, tokenType 은 분류된 토큰의 종류를 말한다. 그리고 해당 토큰이 몇 번째 라인에서 발견됐는지 알려주는 line 변수. (에러를 표시할 때 유용하게 쓰인다.)

struct Token {
    std::string str;
    TokenType tokenType;
    int line;
    Token(std::string s, TokenType tt, int l = 0) {
        str = s;
        tokenType = tt;
        line = l;
    }
}; 

TokenType 은 다음과 같다. 각종 예약어와 while, for, 지시어등이 담긴다.

enum class TokenType {
    none,
    _var,
    _func,
    integer,
    id,
    assign,
    decassign,
    lbracket,
    rbracket,
    semicolon,
    logical_or,
    logical_and,
    equal,
    not_equal,
    less_than, // <
    less_than_equal, // <=
    greater_than, // >
    greater_than_equal,  // >=
    plus,
    minus,
    mult,
    devide,
    modular,
    lparen,
    rparen,
    comma,
    _if,
    _while,
    _break,
    _continue,
    _return,
    eof
};
struct Token {
    std::string str;
    TokenType tokenType;
    int line;
    Token(std::string s, TokenType tt, int l = 0) {
        str = s;
        tokenType = tt;
        line = l;
    }
}; 
struct BlockInfo {
    std::string sLabel = "SLABEL";
    std::string eLabel = "EkhBEL";
}; 

우리가 작성할 Parser

class KParser {
    public:
        std::vector<std::pair<int, std::string>> m_tree; ///< 
        std::vector<Token> m_tokens; ///< 토큰화된 소스가 담기는 곳
        std::vector<std::vector<std::string>> m_il; ///< 파싱이 진행되면서 중간 생성코드가 여기에 저장된다.
        std::vector<std::string> m_errorMsgs; ///< 파싱이 실패했을 때, 에러메시지가 담긴다.  
        std::map<std::string, int> m_funcToParams; ///< 어떤 함수가 몇 개의 인자가 있는지 
        std::map<std::string, int> m_varToLocal; ///< 어떤 변수가 몇 번째 지역변수인지 저장
        int m_increaseLocals = 0; ///< 함수에서 지역변수가 선언이 될 때 증가함. 새로운 함수 진입시 0 으로 초기화.
        std::map<std::string, int> m_varToArg; ///< 해당 변수가 몇 번째 argument 인지 저장하는 테이블.
        int m_increaseArgs = 0; ///< m_varToArg 보조하기 위한 변수

        int m_parameters = 0;///< m_varToArg 보조하기 위한 변수

        ...

Parser 구현부

int KParser::_root(int begin,  BlockInfo& bi) {
    int i = begin;
    int t = 0;

    while (i < m_tokens.size()) {
        t = _external(i, bi);
        i = t;
        if (t < 0) {
            break;
        }
        i = t;
    }
    return i; 
}
int KParser::_external(int begin,  BlockInfo& bi) { 
    int i = begin;
    int t = 0;

    if (m_tokens[i].tokenType == TokenType::eof) {
        i++;
        return i;
    }

    t = _func(i, bi); 
    if (t >= 0) {
        m_errorMsgs.clear();
        return t;
    }

    t = _var(i, bi); 
    if (t >= 0) {
        return t;
    }
    addErrorString(i, "함수 선언이나 변수 선언이 와야 합니다.");
    return -1;
}
int KParser::_func(int begin,  BlockInfo bi) {
    int i = begin;
    int t = 0; 
    if (m_tokens[i].tokenType != TokenType::_func) {
        return -1;
    }
    i++; 
    if (m_tokens[i].tokenType != TokenType::id) {
        addErrorString(i, "func 뒤에는 식별자가 와야합니다.");
        return -1;
    } 
    std::string funcName = m_tokens[i].str;
    m_codes.clear();
    addCode("func", funcName, "");
    i++; 
    m_parameters = 0; 
    t = _parameters(i, bi);
    if (t < 0) {
        addErrorString(i, "함수인자 정의가 잘못 됐습니다.");
        return -1;
    }
    m_funcToParams[funcName] = m_parameters;
    i = t;

    m_locals = 0;
    m_increaseLocals = 0;
    t = _block(i, bi);
    if (t < 0) {
        addErrorString(i, "함수몸체 정의가 잘못 됐습니다.");
        return -1;
    }
    i = t;
    m_codes[0][2] = std::to_string(m_locals); 
    for(auto& i: m_codes) {
        if(m_varToArg.find(i[1]) != m_varToArg.end()) {
            i[2] = std::to_string(m_varToArg[i[1]]);
            i[1] = "argument";
        }
        if(m_varToLocal.find(i[1]) != m_varToLocal.end()) {
            i[2] = std::to_string(m_varToLocal[i[1]]);
            i[1] = "local";
        }
    }

    m_il.insert(m_il.end(), m_codes.begin(), m_codes.end());
    return i;
}
int KParser::_parameters(int begin,  BlockInfo bi) {
    int i = begin;
    int t = 0;
    if (m_tokens[i].tokenType != TokenType::lparen) {
        addErrorString(i, "( 가 빠졌습니다.");
        return -1;
    }
    i++;

    if (m_tokens[i].tokenType == TokenType::rparen) {
        i++;
        return i;
    }
    // 이 까지 왔으면 매개변수가 하나이상 있는 것이다.
    int idIndex = i;
    if (m_tokens[idIndex].tokenType != TokenType::id) {
        addErrorString(idIndex, "함수 인자에는 식별자가 와야합니다.");
        return -1;
    }
    i++;
    m_varToArg[m_tokens[idIndex].str] = m_parameters;
    m_parameters++;

    while (1) {
        int tryI = i;
        std::string rightValue = m_tokens[tryI].str;
        TokenType tokenValue = m_tokens[tryI].tokenType;
        if(tokenValue != TokenType::comma) {
            break;
        }
        tryI++;
        if (m_tokens[tryI].tokenType != TokenType::id) {
            addErrorString(tryI, ", 뒤에 식별자를 기대했습니다.");
            return -1; 
        }
        m_varToArg[m_tokens[tryI].str] = m_parameters;
        m_parameters++;
        tryI++;
        i = tryI;
    } 
    if (m_tokens[i].tokenType != TokenType::rparen) {
        addErrorString(i, ", 뒤에 식별자를 기대했습니다.");
        return -1;
    }
    i++;
    return i;
}
int KParser::_block(int begin,  BlockInfo bi) {
    int i = begin;
    int t;
    if(m_tokens[i].tokenType != TokenType::lbracket) {
        return -1;
    }
    i++;
    while(1) {
        if(m_tokens[i].tokenType == TokenType::rbracket) {
            i++;
            break;
        }
        t = _statement(i, bi);
        if (t < 0) {
            char buf[256];
            sprintf(buf, "line[%d]: 문장이 아닙니다.\n", m_tokens[i].line);
            m_errorMsgs.push_back(buf);
            return -1;
        }
        i = t; 
    }
    return i; 
}
int KParser::_var(int begin,  BlockInfo bi) {
    int i = begin;
    int t = 0;

    if (m_tokens[i].tokenType != TokenType::_var) {
        return -1;
    }
    i++;

    if (m_tokens[i].tokenType != TokenType::id) {
        char buf[256];
        sprintf(buf, "line[%d]: 식별자가 빠졌습니다.\n", m_tokens[i].line);
        m_errorMsgs.push_back(buf);
        return -1;
    }
    i++;

    if (m_tokens[i].tokenType != TokenType::semicolon) {
        char buf[256];
        sprintf(buf, "line[%d]: ; 가 빠졌습니다.\n", m_tokens[i].line);
        m_errorMsgs.push_back(buf);
        return -1;
    }
    i++;
    return i;
}
int KParser::_statement(int begin,  BlockInfo bi) {
    int i = begin;
    int t = 0;
    if((t = _if(i, bi)) >= 0) {
        i = t;
    } else if((t = _while(i, bi)) >= 0) {
        i = t;
    }else if((t = _assign(i, bi)) >= 0) {
        i = t;
    } else if((t = _decassign(i, bi)) >= 0) {
        i = t;
    } else if((t = _block(i, bi)) >= 0) {
        i = t;
    } else if((t = _break(i, bi)) >= 0) {
        i = t;
    } else if((t = _continue(i, bi)) >= 0) {
        i = t;
    }else if((t = _simple_expr(i, bi)) >= 0) {
        i = t;
    } else if((t = _return(i, bi)) >= 0) {
        i = t;
    } else {
        return -1;
    }
    return i;
}

int KParser::_call(int begin,  BlockInfo bi) {
    // some();
    // some(a);
    // some(a,b,c);
    int i = begin;
    int t = 0;

    // 인자가 없는 경우 
    if (m_tokens[i].tokenType == TokenType::id && 
        m_tokens[i + 1].tokenType == TokenType::lparen && 
        m_tokens[i + 2].tokenType == TokenType::rparen
        ) {
        addCode("call", m_tokens[i].str,  std::to_string( m_funcToParams[m_tokens[i].str]));
        return i + 3;
    }

    // 인자가 하나이상 있는 경우
    if (m_tokens[i].tokenType == TokenType::id && 
        m_tokens[i + 1].tokenType == TokenType::lparen) {
        i = i + 2;
        // 이 까지 왔으면 매개변수가 하나이상 있는 것이다.
        t = _expr(i, bi);
        if (t < 0) {
            addErrorString(i, "함수 인자에는 표현식이 와야합니다.");
            return -1;
        }
        i = t;

        while (1) {
            int tryI = i;
            std::string rightValue = m_tokens[tryI].str;
            TokenType tokenValue = m_tokens[tryI].tokenType;
            if(tokenValue != TokenType::comma) {
                break;
            }
            tryI++;
            t = _expr(tryI, bi);
            if (t < 0) {
                addErrorString(i, ", 뒤에 표현식을 기대했습니다.");
                return -1; 
            }
            tryI = t;
            i = tryI;
        } 
        if (m_tokens[i].tokenType != TokenType::rparen) {
            addErrorString(i, "_call: ) 가 빠졌습니다.");
            return -1;
        }
        i++;
        addCode("call", m_tokens[begin].str,  std::to_string( m_funcToParams[m_tokens[begin].str]));
        return i;
    }

    return -1; 
}

int KParser::_return(int begin,  BlockInfo bi) {
    int i = begin;
    int t = 0;

    if (m_tokens[i].tokenType != TokenType::_return) {
        return -1;
    }
    i++;

    if ((t = _expr(i, bi)) < 0) {
        addErrorString(i, "표현식이 와야합니다.");
        return -1;
    } 
    i = t;
    if (m_tokens[i].tokenType != TokenType::semicolon) {
        addErrorString(i, "; 이 와야합니다.");
        return -1;
    }
    i++;
    addCode("return", "", "");
    return i;
}

int KParser::_expr(int begin,  BlockInfo bi) {
    return _adv_expr(begin, bi);
}

int KParser::_simple_expr(int begin,  BlockInfo bi) {
    // <expr>;
    int i = begin;
    int t = 0;
    if ((t = _expr(i, bi)) < 0) {
        return -1;
    }
    i = t;
    if (m_tokens[i].tokenType != TokenType::semicolon) {
        addErrorString(i, "; 이 와야합니다.");
        return -1;
    }
    i++;
    // 대입문이 없으므로 빈 스택을 pop 해준다.
    addCode("pop", "", ""); 
    return i;
}
int KParser::_adv_expr(int begin,  BlockInfo bi) {
    return _or_expr(begin, bi);
}

int KParser::_or_expr(int begin,  BlockInfo bi) {
    int i = begin;
    int t;
    std::string leftValue = m_tokens[i].str;
    t = _and_expr(i, bi);
    if (t < 0) {
        return -1;
    }
    i = t;
    while (1) {
        int tryI = i;
        std::string rightValue = m_tokens[tryI].str;
        TokenType tokenValue = m_tokens[tryI].tokenType;
        if(tokenValue != TokenType::logical_or) {
            break;
        }
        tryI++;
        t = _and_expr(tryI, bi);
        if (t < 0) {
            break;
        }
        addCode("or", "", "");
        i = t; 
    } 
    return i;
}

int KParser::_and_expr(int begin,  BlockInfo bi) {
    int i = begin;
    int t;
    std::string leftValue = m_tokens[i].str;
    t = _cmp_expr(i, bi);
    if (t < 0) {
        return -1;
    }
    i = t;
    while (1) {
        int tryI = i;
        std::string rightValue = m_tokens[tryI].str;
        TokenType tokenValue = m_tokens[tryI].tokenType;
        if(tokenValue != TokenType::logical_and) {
            break;
        }
        tryI++;
        t = _cmp_expr(tryI, bi);
        if (t < 0) {
            break;
        }
        // bi.addCode(leftValue);
        // bi.addCode(rightValue);
        addCode("and", "", "");
        i = t; 
    } 
    return i;
}

int KParser::_cmp_expr(int begin,  BlockInfo bi) {
    int i = begin;
    int t;
    std::string leftValue = m_tokens[i].str;
    t = _add_expr(i, bi);
    if (t < 0) {
        return -1;
    }
    i = t;

    do {
        int tryI = i;
        auto tokenValue = m_tokens[tryI].tokenType;
        if(tokenValue != TokenType::equal 
           && tokenValue != TokenType::not_equal
           && tokenValue != TokenType::less_than
           && tokenValue != TokenType::less_than_equal
           && tokenValue != TokenType::greater_than
           && tokenValue != TokenType::greater_than_equal
           ) {
            break;
        }
        tryI++;
        std::string rightValue = m_tokens[tryI].str;
        t = _add_expr(tryI, bi);
        if (t < 0) {
            break;
        }
        // bi.addCode(leftValue);
        // bi.addCode(rightValue);
        if(tokenValue == TokenType::equal) {
            addCode("eq", "", "");
        } else if(tokenValue == TokenType::not_equal) {
            addCode("neq", "", "");
        }else if(tokenValue == TokenType::less_than) {
            printf("lt!!\n");
            addCode("lt", "", "");
        }else if(tokenValue == TokenType::less_than_equal) {
            printf("lte!!\n");
            addCode("lte", "", "");
        }else if(tokenValue == TokenType::greater_than) {
            addCode("gt", "", "");
        }else if(tokenValue == TokenType::greater_than_equal) {
            addCode("gte", "", "");
        } 
        i = t; 
    } while(0);
    return i;
}
int KParser::_add_expr(int begin,  BlockInfo bi) {
    int i = begin;
    int t;
    std::string leftValue = m_tokens[i].str;
    t = _mul_expr(i, bi);
    if (t < 0) {
        return -1;
    }
    i = t;
    while (1) {
        int tryI = i;
        auto tokenValue = m_tokens[tryI].tokenType;
        if(tokenValue != TokenType::plus && tokenValue != TokenType::minus) {
            break;
        }
        tryI++;
        std::string rightValue = m_tokens[tryI].str;
        t = _mul_expr(tryI, bi);
        if (t < 0) {
            break;
        }
        if(tokenValue == TokenType::plus) {
            addCode("add", "", "");
        } else if(tokenValue == TokenType::minus) {
            addCode("sub", "", "");
        }
        i = t; 
    } 
    return i;
}
int KParser::_mul_expr(int begin,  BlockInfo bi) {
    int i = begin;
    int t;
    std::string leftValue = m_tokens[i].str;
    t = _sign_expr(i, bi);
    if (t < 0) {
        return -1;
    }
    i = t;
    while (1) {
        int tryI = i;
        auto tokenValue = m_tokens[tryI].tokenType;
        t = _mul_op(tryI, bi);
        if(t < 0) {
            break;
        }
        tryI++;
        std::string rightValue = m_tokens[tryI].str;
        t = _sign_expr(tryI, bi);
        if (t < 0) {
            break;
        }
        if(tokenValue == TokenType::mult) {
            addCode("mult", "", "");
        } else if(tokenValue == TokenType::devide) {
            addCode("div", "", "");
        } else if(tokenValue == TokenType::modular) {
            addCode("mod", "", "");
        }

        i = t; 
    } 
    return i;
}

int KParser::_mul_op(int begin,  BlockInfo bi) {
    int i = begin;
    if (m_tokens[i].tokenType != TokenType::mult && m_tokens[i].tokenType != TokenType::devide && 
        m_tokens[i].tokenType != TokenType::modular) {
        return -1;
    }
    i++;
    return i;
}
int KParser::_sign_expr(int begin,  BlockInfo bi) {
    int i = begin;
    bool isMinus = false;
    if(m_tokens[i].tokenType == TokenType::minus) {
        isMinus = true;
        i++;
    } 
    int t = _factor_expr(i, bi);
    if(t < 0) {
        return -1;
    }
    if (isMinus) {
        addCode("neg", "", "");
    }
    return t;
}

int KParser::_factor_expr(int begin,  BlockInfo bi) {
    int i = begin;
    TokenType tokenType = m_tokens[i].tokenType;
    int t = 0;

    if((t = _call(i, bi)) >= 0) {
        i = t;
    } else if(tokenType == TokenType::id) {
        addCode("push", m_tokens[i].str, "");
        i++;
    } else if(tokenType == TokenType::integer) {
        addCode("push", "constant", m_tokens[i].str);
        i++;
    } else if(tokenType == TokenType::lparen) {
        int tryI = i;
        tryI++;
        int t = _expr(tryI, bi);
        if(t < 0) {
            addErrorString(i, "표현식의 오류.");
            return -1;
        }
        if(m_tokens[t].tokenType != TokenType::rparen) {
            addErrorString(i, "닫는 괄호가 빠졌습니다.\n");
            return -1; 
        }
        t++;
        i = t;
    } 
    else {
        return -1;
    } 
    return i;
}


int KParser::_if(int begin,  BlockInfo bi) {
    int i = begin;
    int t = 0;
    if( m_tokens[i].tokenType != TokenType::_if ) {
        return -1;
    }
    i++;

    if((t = _expr(i, bi)) < 0) {
        return -1;
    } else {
        i = t;
    } 
    std::string eLabel = "LABEL" + std::to_string(m_labelNumber++);

    addCode("jz", eLabel, "");
    if((t = _block(i, bi)) < 0) {
        return -1;
    } else {
        i = t;
    }
    addCode("label", eLabel, "");
    return i;
}

int KParser::_while(int begin,  BlockInfo bi) {
    int i = begin;
    int t = 0;

    if( m_tokens[i].tokenType != TokenType::_while ) {
        return -1;
    }
    i++; 
    std::string sLabel = "LABEL" + std::to_string(m_labelNumber++);
    std::string eLabel = "LABEL" + std::to_string(m_labelNumber++);
    bi.sLabel = sLabel;
    bi.eLabel = eLabel;
    addCode("label", sLabel, "");
    if((t = _expr(i, bi)) < 0) {
        return -1;
    } else {
        i = t;
    } 
    addCode("jz", eLabel, "");


    if((t = _block(i, bi)) < 0) {
        return -1;
    } else {
        i = t;
    }
    addCode("jmp", sLabel, "");
    addCode("label", eLabel, "");
    return i;
}
int KParser::_break(int begin,  BlockInfo bi) {
    int i = begin;
    int t;

    if (m_tokens[i].tokenType != TokenType::_break) {
        return -1;
    } 
    i++;
    if (m_tokens[i].tokenType != TokenType::semicolon) {
        return -1;
    }
    i++;
    addCode("jmp", bi.eLabel, "");
    return i;
}

int KParser::_continue(int begin,  BlockInfo bi) {
    int i = begin;
    int t;

    if (m_tokens[i].tokenType != TokenType::_continue) {
        return -1;
    } 
    i++;
    if (m_tokens[i].tokenType != TokenType::semicolon) {
        return -1;
    }
    i++;
    addCode("jmp", bi.sLabel, "");
    return i;
}

int KParser::_assign(int begin,  BlockInfo bi) {
    int i = begin;
    int t;
    int idIndex = i;
    if (m_tokens[i].tokenType != TokenType::id) {
        return -1;
    } 
    i++; 
    if (m_tokens[i].tokenType != TokenType::assign) {
        return -1;
    } 
    i++; 

    int exprIndex = i;
    t = _expr(i, bi);
    if (t < 0) {
        char buf[256];
        sprintf(buf, "line[%d]: 표현식의 오류\n", m_tokens[i].line); 
        m_errorMsgs.push_back(buf);
        return -1;
    } 
    i = t;

    if (m_tokens[i].tokenType != TokenType::semicolon) {
        char buf[256];
        sprintf(buf, "line[%d]: ; 가 빠졌습니다.\n", m_tokens[i].line); 
        m_errorMsgs.push_back(buf);
        return -1;
    }
    i++;
    addCode("pop", m_tokens[idIndex].str, "");
    return i;
} 

int KParser::_decassign(int begin,  BlockInfo bi) {
    int i = begin;
    int t;
    int idIndex = i;
    if (m_tokens[i].tokenType != TokenType::id) {
        return -1;
    } 
    i++; 
    if (m_tokens[i].tokenType != TokenType::decassign) {
        return -1;
    } 
    i++; 

    int exprIndex = i;
    t = _expr(i, bi);
    if (t < 0) {
        char buf[256];
        sprintf(buf, "line[%d]: decassign 표현식의 오류\n", m_tokens[i].line); 
        m_errorMsgs.push_back(buf);
        return -1;
    } 
    i = t;

    if (m_tokens[i].tokenType != TokenType::semicolon) {
        char buf[256];
        sprintf(buf, "line[%d]: _decassign ; 가 빠졌습니다.\n", m_tokens[i].line); 
        m_errorMsgs.push_back(buf);
        return -1;
    }
    i++;
    m_locals++;
    addCode("pop", m_tokens[idIndex].str, "");
    m_varToLocal[m_tokens[idIndex].str] = m_increaseLocals++;
    printf("%s => %d\n",  m_tokens[idIndex].str.c_str(), m_increaseLocals);
    return i;
}

굉장히 길다. 길기만 길지 어려운 부분은 없다. 앞서 정의한 EBNF 를 그대로 코드로 옮겨논 것에 불과하다. 이 옮기는 과정에서 버그는 나올 수 있고, 내가 작성한 코드 역시 버그가 있을 수 있다. 앞장에서 정의한 EBNF 과 이 코드와 비교하면서 읽어보기를 권한다.

지역변수, 함수인자의 심볼 제거

우리는 가상머신을 정의할 때 segment index 식으로 표기하기로 했다. 즉 가상머신코드로 변환이 될 때 지역변수이름, 함수인자이름 대신에 index 형태로 나타나야한다는 것을 뜻한다. 어떻게 숫자를 부여하며 구현했는지 설명한다.

여기서 가상머신과 결합하는 부분의 포인트만 짚고 넘어간다. _decassign 함수 부분은 지역변수를 선언하는 부분이다. decassign 함수의 m_varToLocal[m_tokens[idIndex].str] = m_increaseLocals++; 를 보면 함수에서 쓰인 지역변수가 몇 번째 지역변수인지 탐색된 순서부터 숫자가 부여된다. 함수 해석이 다 끝날 때 _func 함수의 끝에 보면

for(auto& i: m_codes) {
    if(m_varToArg.find(i[1]) != m_varToArg.end()) {
        i[2] = std::to_string(m_varToArg[i[1]]);
        i[1] = "argument";
    }
    if(m_varToLocal.find(i[1]) != m_varToLocal.end()) {
        i[2] = std::to_string(m_varToLocal[i[1]]);
        i[1] = "local";
    }
}

여기서 push variable_name 와 같은 형식이 push local 0 와 같은 형식으로 바뀌게 된다. 이는 변수 선언할 때 m_varToLocal 로 인해 매겨진 순서에 의해 변환이 된다. local 뿐 아니라 arg 도 마찬가지로 심볼릭에서 인자의 숫자로 변환된다.

함수의 인자의 개수 구하기

가상머신코드를 정의할 때 caller 쪽에서 부르는 함수가 인자가 몇 갠지 알아야 한다고 했다. _func 함수 밑에 parameter 해석하는 부분이 끝난 후 m_funcToParams 테이블에 어떤 함수이름이 몇 개의 인자를 가지는지 m_parameters 로 조사한 후, 테이블에 값을 넣는다.

m_funcToParams[funcName] = m_parameters;

그리고 _call 부분에서 함수이름을 이용하여 바이트코드를 완성시킨다.

가상머신 구현

이상 앞에서 다룬 부분은 구문분석 단계에서 스택기반의 코드를 생성하는 것이 목표였다. 이제 생성된 스택기반의 코드를 실행하는 실행기를 만들어야 한다.

class Vm {
    public:
        static const int STACK_FRAME_SIZE = 3;
        std::vector<std::vector<std::string>> m_program; 
        std::vector<int> m_stack; 
        std::map<std::string, int> m_labelToEip;
        std::map<std::string, int> m_funcToEip;
        int m_eip = 0;
        int m_argPointer = 0;
        int m_localPointer = 0;
        int m_stackPointer = 0;

        ...

vm.h 정의 부분이다. 4장에서 jump label 을 다루는 변수는 m_labelToEip 다. 이 변수는 label 에서 eip 로의 테이블이다. 마찬가지로 m_funcToEip 역시 어떤 함수가 몇 번째 코드에 있는지 나타낸다.

어떻게 테이블을 채우나?


main.cpp:
    kparser.parse();
    Vm vm;
    for(auto i: kparser.m_il) {
        vm.addCommand(i);
    }

vm.h:
    void addCommand(std::vector<std::string> k) {
        if (k[0] == "func") {
            m_funcToEip[k[1]] = m_program.size();
        } else if(k[0] == "label") {
            m_labelToEip[k[1]] = m_program.size();
        }
        m_program.push_back(k);
    }

명령어가 들어오면 현재 m_program.size() 가 하나씩 증가하는데 레이블과 관련된 func, label 키워드가 들어오면 labelToEip 테이블에 값을 넣는다. 추후 call 이나 jump 명령어가 들어오면 이 테이블을 참조하여 eip 를 얻는다.

명령어의 실행

        int step() {
            if (m_program[m_eip].size() && m_eip <= m_program.size() - 1) {
                auto cmd = m_program[m_eip][0]; 
                if (cmd == "neq") {
                    int t1 = getStackTop();
                    m_stack.pop_back();
                    int t2 = getStackTop();
                    m_stack.pop_back(); 
                    m_stack.push_back(t1 != t2);
                    m_eip++;
                } else if (cmd == "eq") {
                    int t1 = getStackTop();
                    m_stack.pop_back();
                    int t2 = getStackTop();
                    m_stack.pop_back(); 
                    m_stack.push_back(t1 == t2);
                    m_eip++;
                } 
                else if (cmd == "lt") {
                    int t1 = getStackTop();
                    m_stack.pop_back();
                    int t2 = getStackTop();
                    m_stack.pop_back(); 
                    m_stack.push_back(t2 < t1);
                    m_eip++;
                }else if (cmd == "lte") {
                    int t1 = getStackTop();
                    m_stack.pop_back();
                    int t2 = getStackTop();
                    m_stack.pop_back(); 
                    m_stack.push_back(t2 <= t1);
                    m_eip++;
                }else if (cmd == "gt") {
                    int t1 = getStackTop();
                    m_stack.pop_back();
                    int t2 = getStackTop();
                    m_stack.pop_back(); 
                    m_stack.push_back(t2 > t1);
                    m_eip++;
                }else if (cmd == "gte") {
                    int t1 = getStackTop();
                    m_stack.pop_back();
                    int t2 = getStackTop();
                    m_stack.pop_back(); 
                    m_stack.push_back(t2 >= t1);
                    m_eip++;
                } 
                else if (cmd == "add") {
                    int t1 = getStackTop();
                    m_stack.pop_back();
                    int t2 = getStackTop();
                    m_stack.pop_back(); 
                    m_stack.push_back(t1 + t2);
                    m_eip++;
                } else if (cmd == "sub") {
                    int t1 = getStackTop();
                    m_stack.pop_back();
                    int t2 = getStackTop();
                    m_stack.pop_back(); 
                    m_stack.push_back(t2 - t1);
                    m_eip++;
                } else if (cmd == "mult") {
                    int t1 = getStackTop();
                    m_stack.pop_back();
                    int t2 = getStackTop();
                    m_stack.pop_back(); 
                    m_stack.push_back(t2 * t1);
                    m_eip++;
                } else if (cmd == "div") {
                    int t1 = getStackTop();
                    m_stack.pop_back();
                    int t2 = getStackTop();
                    m_stack.pop_back(); 
                    m_stack.push_back(t2 / t1);
                    m_eip++;
                } else if (cmd == "mod") {
                    int t1 = getStackTop();
                    m_stack.pop_back();
                    int t2 = getStackTop();
                    m_stack.pop_back(); 
                    m_stack.push_back(t2 % t1);
                    m_eip++;
                } else if (cmd == "neg") {
                    int t1 = getStackTop();
                    m_stack.pop_back();
                    m_stack.push_back(-t1);
                    m_eip++;
                } else if (cmd == "and") {
                    int t1 = getStackTop();
                    m_stack.pop_back();
                    int t2 = getStackTop();
                    m_stack.pop_back(); 
                    m_stack.push_back(t2 && t1);
                    m_eip++;
                } else if (cmd == "or") {
                    int t1 = getStackTop();
                    m_stack.pop_back();
                    int t2 = getStackTop();
                    m_stack.pop_back(); 
                    m_stack.push_back(t2 || t1);
                    m_eip++;
                }

                else if (cmd == "push") {
                    if (m_program[m_eip].size() < 3) {
                        return -1;
                    } 
                    auto next = m_program[m_eip][1];
                    if (next == "local") {
                        auto pos = std::stoi(m_program[m_eip][2]);
                        m_stack.push_back(m_stack[m_localPointer + pos]);
                    } else if (next == "argument") {
                        auto pos = std::stoi(m_program[m_eip][2]);
                        m_stack.push_back(m_stack[m_argPointer + pos]);
                    } else if (next == "constant") {
                        auto pos = std::stoi(m_program[m_eip][2]);
                        m_stack.push_back(pos);
                    } else {
                        return -1;
                    }
                    m_eip++;
                } else if (cmd == "pop") {
                    if (m_program[m_eip].size() < 3) {
                        m_stack.pop_back();
                    } else {
                        auto next = m_program[m_eip][1];
                        if (next == "local") {
                            auto pos = std::stoi(m_program[m_eip][2]);
                            m_stack[m_localPointer + pos] = getStackTop();
                            m_stack.pop_back();
                        } else if (next == "argument") {
                            auto pos = std::stoi(m_program[m_eip][2]);
                            m_stack[m_argPointer + pos] = getStackTop();
                            m_stack.pop_back();
                        } else if (next == "constant") {
                            // not op
                        } else if (next == "") {
                            m_stack.pop_back(); 
                        } else {
                            return -1;
                        }
                    }
                    m_eip++;
                } 
                else if (cmd == "label") {
                    m_eip++;
                } else if (cmd == "jmp") {
                    auto next = m_program[m_eip][1];
                    m_eip = m_labelToEip[next]; // [next] = m_eip + 1;
                } else if (cmd == "jz") {
                    auto next = m_program[m_eip][1];
                    if (getStackTop() == 0) {
                        m_eip = m_labelToEip[next];
                    } else {
                        m_eip++;
                    }
                    m_stack.pop_back(); 
                } else if (cmd == "jnz") {
                    auto next = m_program[m_eip][1];
                    if (getStackTop() != 0) {
                        m_eip = m_labelToEip[next];
                    } else {
                        m_eip++;
                    }
                    m_stack.pop_back(); 
                } 
                else if (cmd == "call") {
                    // call fn 2
                    if (m_program[m_eip].size() < 3) {
                        std::cout << "<3\n";
                        return -1;
                    }
                    auto fn = m_program[m_eip][1];
                    if(fn == "print") {
                        int t = *m_stack.rbegin();
                        // m_stack.pop_back(); 
                        std::cout << t << "\n"; //  << "built-in-function\n";
                        m_eip++;
                    } else {
                        int argNumber = std::stoi(m_program[m_eip][2]);
                        int oldLocalPointer = m_localPointer;
                        int oldArgPointer = m_argPointer;
                        m_argPointer = m_stack.size() - argNumber;
                        m_stack.push_back(m_eip + 1);
                        m_stack.push_back(oldLocalPointer);
                        m_stack.push_back(oldArgPointer);
                        m_localPointer = m_stack.size();
                        m_eip = m_funcToEip[fn]; 
                    }
                } else if (cmd == "func") {
                    // call fn 2
                    if (m_program[m_eip].size() < 3) {
                        return -1;
                    }
                    auto fn = m_program[m_eip][1];
                    int locals = std::stoi(m_program[m_eip][2]);
                    for(int i=0; i<locals; i++) {
                        m_stack.push_back(0); // 지역변수를 전부 0 으로 초기화 함.
                    }
                    m_eip++;
                } else if (cmd == "return") {
                    int frame = m_localPointer;
                    int ret = m_stack[frame - STACK_FRAME_SIZE]; // 돌아갈 주소
                    m_stack[m_argPointer] = getStackTop();
                    int pops = m_argPointer + 1; // 해당 함수에서 단 하나의 숫자가 남을 때 까지 팝하기 위해서
                    m_argPointer = m_stack[frame - 1];
                    m_localPointer = m_stack[frame - 2];
                    while (m_stack.size() > pops) {
                        m_stack.pop_back();
                    }
                    m_eip = ret; 
                } else if (cmd == "exit") {
                    return -1;
                } 
                return 0;
            } 
            return -1;
        } 

step 함수의 구현부의 일부는 이전장에서 다뤘었다.

push, pop, return, func, call, jnz, jz 부분을 중심으로 테이블 변수가 어떻게 쓰이는지 스택 프레임을 어떻게 구성하는지 주의깊게 보면 좋겠다.

전체 Source

https://github.com/hsnks100/dreampiler

전체 소스는 위 github 에서 배포한다. 테스트코드는 main.cpp 에 하드코딩으로 넣어놨다. 파일로 빼는 것이 좋겠지만, 상용적으로 쓰이는 것이 아닌 학습의 목적이 있기 때문에 따로 테스트코드를 파일로 처리하진 않았다.

Updated:

Comments