저번에 의해 간단한 언어 Hum을 컴파일 하는 과정을 다시 한번 해보겠다. 

그 전에 컴파일이라고해서 바로 기계어로 변환되는것은 아니다. 컴파일은 기계어에 가까워지게 바뀐다면 그 자체로 컴파일이라 하고 그 반대라면 디컴파일이라고 한다. 

 

translate함수를 구현하는 중 

{ or } 왔을경우의 대처와 정수가 입력되었을 떄 그 값을 어떻게 하면 사용할 수 있을까 고민에 빠짐.

-구조 설계-

머릿속으로 구조적 설계 했을 때 공백 간격으로 문자열들을 받아 Hul* pattern으로 이루어진 string은 tokens arrayList에 저장 그리고 정수가 들어온다면 변수 선언과 반복횟수는 숫자 순서 출력이 반대이기에 dequeue를 쓸 생각을 했다. 

그리고 tokenList에 있는 값들마다 token을 부여하고 부여된 token에 저장되어 있는 출력값을 읽어준다. 

 

---------------------------------------------

-코딩-

이제 실전 코딩 시작!!!

먼저 Hul?, Hul>, Hul! 와 같은 경우에는 들여쓰기도 필요 없고 Token에 각 해당되는 enum타입이 들어오면 그대로 출력해준다. 

-입력된 값들 control 하고 해당하는 토큰으로 변환해주는 parser class


public class Parser {

    public enum Token{
        READ("Hul?"), WRITE("Hul!"), INC("Hul>"), DEC("Hul<"),
        BLOCK_BEGIN("Hul{"), BLOCK_END("}"), FAIL("fail");
        String str;
        Token(String s) {
            this.str =s;
        }
    }
    private ArrayList<Token> tokens = new ArrayList<>();
    //가장 바깥자료가 마지막에 위치하고 바깥자료 먼저 출력해야함으로 스텍 쓰기
    private Deque<Integer> maxList = new ArrayDeque<>();

    public Deque<Integer> getMaxList() {
        return maxList;
    }

    public ArrayList<Token> getTokens() {
        return tokens;
    }

    public Parser(String msg) {
        parsing(msg);
    }

    public void parsing(String msg){
        //split함수 이용해 msg들 분리
        String[] msgList = msg.split("[\n\t \r\n]+");

        //리스트에 정수가 있다면 따로 처리하기 위해 분리함
        for(String s: msgList){
            if(s.matches("[+-]?\\d*(\\.\\d+)?")){
                //문자열이 정수인지 확인 후 정수면 deque에 저장
                maxList.push(Integer.parseInt(s));
            }
            else{
                tokens.add(findToken(s));
            }
        }
    }
    private Token findToken(String msg){
        if(msg.equals(Token.BLOCK_BEGIN.str)) return Token.BLOCK_BEGIN;
        if(msg.equals(Token.BLOCK_END.str)) return Token.BLOCK_END;
        if(msg.equals(Token.READ.str)) return Token.READ;
        if(msg.equals(Token.WRITE.str)) return Token.WRITE;
        if(msg.equals(Token.DEC.str)) return Token.DEC;
        if(msg.equals(Token.INC.str)) return Token.INC;
        return Token.FAIL;
    }
}

 

 

그리고 Hul파일에서 C파일로 바꿔는 HulToC클래스 

public class HulToC {
    /**
     *
     Hul? standard input으로 정수를 읽어서 변수에 저장한다.
     Hul? standard output으로 변수를 출력한다.
     Hul> 변수의 값을 1증가시킨다.
     Hul< 변수의 값을 1감소시킨다.
     Hul{ <명령들> } <반복 횟수>
     <명령들>을 <반복 횟수>번 수행한다
     */
    Parser parser;
    FileWriter fw;
    String hulText;
    // 들여쓰기 { -> ++, } ->  --
    static int lines=1;
    //정수가 입력되었을 때 순서를 부여하기 위한 값 
    static int countNum=0;


    public Parser getParser() {
        return parser;
    }

    public HulToC(String msg, FileWriter fw) throws IOException {
        this.fw = fw;
        //기본 셋팅 항상 일관되게
        this.hulText=msg;
        this.fw.append("#include <stdio.h>\nint main() {\n\tint _hul;\n");
        
        //Parser객체를 생성과 동시에 void parsing함수로 인해 tokens혹은 numlist에 저장됌  
        parser = new Parser(msg);
        //입력된 정수들 이터레이터로 꺼내주고 변수로 만들어주기
        Iterator<Integer> itr = this.getParser().getMaxList().iterator();
        //정수가 입력된 갯수만큼만 만들어줘야함 
        int count= this.getParser().getMaxList().size();
        for(int i=0; i<count;i++) {
            this.fw.append("\tint max" + i + " = " + itr.next() + ";\n");
        }

    }
    //tokens를 순회할 함수 
    public Parser.Token next() {
        if(parser.getTokens().isEmpty()){
            return null;
        }
        //return parser.getTokens().get(0);
        return parser.getTokens().remove(0);
    }

    //이상태는 이미 tokens와 maxList가 다 완성 되어있는 상태 각 값들마다 출력형식을 정한다,. 
    public void translate(Parser.Token token) throws IOException {
        switch (token){
            case READ:{
                //standard input으로 정수를 읽어서 변수에 저장한다.
                fw.append(("\t").repeat(lines)+"printf(\"input: \");\n"+("\t").repeat(lines)+"scanf(\"%d\", &_hul);\n");
                break;
            }
            case WRITE:{
                //standard output으로 변수를 출력한다.
                fw.append(("\t").repeat(lines)+"printf(\"%d\", _hul);\n");
                break;
            }
            case INC:{
                //변수의 값을 1증가시킨다.
                fw.append(("\t").repeat(lines)+"_hul++;\n");
                break;
            }
            case DEC:{
                //변수의 값을 1감소시킨다.
                fw.append(("\t").repeat(lines)+"_hul--;\n");
                break;
            }
            //Hul{ <명령들> } <반복 횟수>
            //<명령들>을 <반복 횟수>번 수행한다.
            case BLOCK_BEGIN:{
                fw.append(("\t").repeat(lines)+"for (int i"+countNum+"=0; i"+countNum+" < max+"+countNum+"; i"+countNum+"++){\n ");
                countNum++;
                lines++;
                break;
            }
            case BLOCK_END:{
                fw.append(("\t").repeat(lines)+"}"+this.getParser().getMaxList().removeLast()+"\n");
                lines--;
                break;
            }
            default: {
                fw.append("문제발생 !");
            }
        }
    }
    //cmd 윈도우 명령어 처리기를 이용하기
}

 

끝! 이제 main class에서 실제로 작동되는지 확인해보자 . 

임의로 String값에 파일이름을 해놓고 ->(나중에 Scanner를 이용해 파일 이름 받는걸로 바꿀거임 ) 

 

public class Test {
    public static void main(String[] args) {

        String msg = "Hul<";
        HulToC hulToC;
        //입력값 확인
        Pattern pattern = Pattern.compile("Hul*");
        Matcher matcher = pattern.matcher(msg);
        System.out.println(matcher.find());
        //읽어오기
        String filePath = "test5.hul";
        try(FileInputStream stream = new FileInputStream(filePath)){
            byte[] rb = new byte[stream.available()];
            ///file이 끝에 도달하면 -1반환
            while(stream.read(rb)!=-1){}
            msg = new String(rb);
           
        }
        catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //쓰기
        try(FileWriter fw = new FileWriter("test.c")) {
            hulToC = new HulToC(msg, fw);//이 순간 모든 command들이 enum타입에 따라 tokens ArrayList에 담김

            while (!hulToC.getParser().getTokens().isEmpty()){
                hulToC.translate(hulToC.next()); //Parser에 있는 tokens

            }
            fw.append("\treturn 0;\n}\n");
            fw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

 

inoput과 output확인 

 

 

벌써부터 머리 아픈 전공생들 사이에서 복전생의 악깡버로 버티기.. 

+ Recent posts