3. Pretty Printer Listener

손지웅·2025년 3월 11일

Java, ANTLR을 이용하여 MiniC 문법으로 된 코드에 대한 Pretty Print 코드를 listener를 활용하여 작성하기

//test.c
int main() {
int a = 5;
 int b = 4;
  int c;
   c = a+b;
}
  1. 초기 설정은 일전의 ugly printer
  2. 블록이나 nesting 되어 들어갈 떄는 4칸 들여쓰기
  3. 2진 연산자와 피 연산자 사이에는 빈칸 1칸
  4. 위의 띄어쓰기들 중첩 가능

import generated.MiniCBaseListener;
import generated.MiniCParser;
import org.antlr.v4.runtime.tree.ParseTreeListener;
import org.antlr.v4.runtime.tree.ParseTreeProperty;

public class MiniCPrintListener extends MiniCBaseListener implements ParseTreeListener{
    int tab = 0;
    private static String output = "";
    ParseTreeProperty<String> cTree = new ParseTreeProperty<>();

    public static String getOutput() {
        return output;
    }

    @Override public void exitProgram(MiniCParser.ProgramContext ctx) {
        StringBuilder program = new StringBuilder();
        for (int i = 0; i < ctx.decl().size(); i++)
            program.append(cTree.get(ctx.decl(i)));
        output = program.toString();
    }

    @Override public void exitDecl(MiniCParser.DeclContext ctx) {
        String result = "";

        if(ctx.fun_decl() != null) {
            result = cTree.get(ctx.fun_decl());
            cTree.put(ctx, result);
        }
        else {
            result = cTree.get(ctx.var_decl());
            cTree.put(ctx, result);
        }

    }

    @Override public void exitVar_decl(MiniCParser.Var_declContext ctx) {
        String type_spec = cTree.get(ctx.type_spec());
        String IDENT = ctx.IDENT().getText();
        String result = "";

        if(ctx.getChildCount() == 3)
            result = type_spec + " " + IDENT + ";\n";
        else if(ctx.getChildCount() == 5)
            result = type_spec + " " + IDENT + " = " + ctx.LITERAL().getText() + ";\n";
        else
            result = type_spec + " " + IDENT + "[" + ctx.LITERAL().getText() + "];\n";

        cTree.put(ctx, result);
    }

    @Override public void exitType_spec(MiniCParser.Type_specContext ctx) {
        String result = "";

        if(ctx.VOID() != null)
            result = "void";
        else
            result = "int";

        cTree.put(ctx, result);
    }
    @Override public void exitFun_decl(MiniCParser.Fun_declContext ctx) {
        String type_spec = cTree.get(ctx.type_spec());
        String IDENT = ctx.IDENT().getText();
        String params = cTree.get(ctx.params());
        String compound_stmt = cTree.get(ctx.compound_stmt());
        String result = "";

        result = type_spec + " " + IDENT + "(" + params + ")" + compound_stmt;

        cTree.put(ctx, result);
    }

    @Override public void exitParams(MiniCParser.ParamsContext ctx) {
        int count = ctx.getChildCount();

        if(count == 0)
            cTree.put(ctx, "");
        else if (ctx.VOID() != null)
            cTree.put(ctx, "void");
        else {
            String params = cTree.get(ctx.param(0));
            for(int i = 1; i < count; i+=2){
                params += ", " + cTree.get(ctx.param(i));
            }
            cTree.put(ctx, params);
        }
    }

    @Override public void exitParam(MiniCParser.ParamContext ctx) {
        String type_spec = cTree.get(ctx.type_spec());
        String IDENT = ctx.IDENT().getText();
        String result = type_spec + " " + IDENT;

        if(ctx.getChildCount() == 3)
            result += "[]";

        cTree.put(ctx, result);
    }

    @Override public void exitStmt(MiniCParser.StmtContext ctx) {
        String result = "";

        if(ctx.expr_stmt() != null)
            result = cTree.get(ctx.expr_stmt());
        else if(ctx.compound_stmt() != null)
            result = cTree.get(ctx.compound_stmt());
        else if(ctx.if_stmt() != null)
            result = cTree.get(ctx.if_stmt());
        else if(ctx.while_stmt() != null)
            result = cTree.get(ctx.while_stmt());
        else
            result = cTree.get(ctx.return_stmt());

        cTree.put(ctx, result);
    }

    @Override public void exitExpr_stmt(MiniCParser.Expr_stmtContext ctx) {
        String result = cTree.get(ctx.expr()) + ";\n";
        cTree.put(ctx, result);
    }

    @Override public void exitWhile_stmt(MiniCParser.While_stmtContext ctx) {
        String result = "while(" + cTree.get(ctx.expr()) + ")" + cTree.get(ctx.stmt());
        cTree.put(ctx, result);
    }

    @Override public void enterCompound_stmt(MiniCParser.Compound_stmtContext ctx) {
        tab++;
    }
    @Override public void exitCompound_stmt(MiniCParser.Compound_stmtContext ctx) {
        StringBuilder result = new StringBuilder();

        for(int i = 0; i < ctx.local_decl().size(); i++)
            result.append("    ".repeat(tab) + cTree.get(ctx.local_decl(i)));
        for(int i = 0; i < ctx.stmt().size(); i++)
            result.append("    ".repeat(tab) + cTree.get(ctx.stmt(i)));

        cTree.put(ctx, "\n" +"    ".repeat(tab - 1) +  "{\n" + result.toString() + "    ".repeat(tab-1) + "}\n");
        tab--;
    }

    @Override public void exitLocal_decl(MiniCParser.Local_declContext ctx) {
        String type_spec = cTree.get(ctx.type_spec());
        String IDENT = ctx.IDENT().getText();
        String result = "";

        if(ctx.getChildCount() == 3)
            result = type_spec + " " + IDENT + ";\n";
        else if(ctx.getChildCount() == 5)
            result = type_spec + " " + IDENT + " = " + ctx.LITERAL().getText() + ";\n";
        else
            result = type_spec + " " + IDENT + "[" + ctx.LITERAL().getText() + "];\n";

        int numOfSilbling = ctx.getParent().getChildCount();

        cTree.put(ctx, result);
    }

    @Override public void exitIf_stmt(MiniCParser.If_stmtContext ctx) {
        String expr = cTree.get(ctx.expr());
        String stmt1 = cTree.get(ctx.stmt(0));
        String result = "";

        if(ctx.getChildCount() == 5)
            result = "if(" + expr + ")" + stmt1;
        else
            result = "if(" + expr + ")" + stmt1 + "    ".repeat(tab) + "else" + cTree.get(ctx.stmt(1));

        cTree.put(ctx, result);
    }

    @Override public void exitReturn_stmt(MiniCParser.Return_stmtContext ctx) {
        String result = "";

        if(ctx.getChildCount() == 2)
            result = "return;\n";
        else
            result = "return " + cTree.get(ctx.expr()) + ";\n";

        cTree.put(ctx, result);
    }

    @Override
    public void exitExpr(MiniCParser.ExprContext ctx) {
        String result = "";
        int childCount = ctx.getChildCount();

        if(childCount == 1)
            result = ctx.getChild(0).getText();
        else if(childCount == 2){
            result = ctx.getChild(0).getText() + cTree.get(ctx.expr(0));
        }
        else if(childCount == 3){
            if(ctx.getChild(0).getText().equals("("))
                result = "(" + cTree.get(ctx.expr(0)) + ")";
            else if(ctx.getChild(1).getText().equals("="))
                result = ctx.IDENT().getText() + " = " + cTree.get(ctx.expr(0));
            else {
                result = cTree.get(ctx.expr(0)) + " " +ctx.getChild(1).getText() + " " + cTree.get(ctx.expr(1));
            }
        }
        else if (childCount == 4) {
            if(ctx.getChild(1).getText().equals("("))
                result = ctx.IDENT().getText() + "(" + cTree.get(ctx.args()) + ")";
            else
                result = ctx.IDENT().getText() + "[" + cTree.get(ctx.expr(0)) + "]";
        }
        else {
            result = ctx.IDENT() + "[" + cTree.get(ctx.expr(0)) + "] = " + cTree.get(ctx.expr(1));
        }

        cTree.put(ctx, result);
    }

    @Override public void exitArgs(MiniCParser.ArgsContext ctx) {
        int count = ctx.getChildCount();

        if(count == 0)
            cTree.put(ctx, "");
        else {
            String args = cTree.get(ctx.expr(0));
            for(int i = 1; i < count; i+=2){
                args += ", " + cTree.get(ctx.expr(i));
            }
            cTree.put(ctx, args);
        }
    }
}

MiniCPrintListener 설명

각 함수별 처리

  1. 프로그램 처리 (exitProgram)

    • 전체 프로그램을 변환하여 output 변수에 저장.
  2. 선언 처리 (exitDecl)

    • 변수 및 함수 선언을 변환.
    • 개행 추가로 가독성 향상.
  3. 변수 선언 처리 (exitVar_decl)

    • int x;, int x = 5;, int arr[10]; 등 변환.
  4. 함수 선언 처리 (exitFun_decl)

    • 반환 타입, 함수 이름, 매개변수, 본문을 변환.
  5. 제어문 처리

    • if-else (exitIf_stmt): 개행 추가하여 가독성 향상.
    • while (exitWhile_stmt): 개행 추가하여 코드 구분 명확화.
    • return (exitReturn_stmt): 개행 추가.
  6. 블록 처리 (exitCompound_stmt)

    • 지역 변수 선언 후 개행 추가.
    • 실행 코드와 구분을 명확히 함.
  7. 표현식 처리 (exitExpr)

    • 연산자, 함수 호출, 배열 접근 등의 표현식을 변환.

메서드 상세 설명

exitProgram(MiniCParser.ProgramContext ctx)

  • 프로그램의 최상위 노드를 처리하여 최종 C 코드 생성.
  • decl()(선언) 노드들을 순회하면서 변환된 코드를 합침.

exitDecl(MiniCParser.DeclContext ctx)

  • 함수 선언(fun_decl)과 변수 선언(var_decl)을 구분하여 처리.
  • 선언 후 개행 추가.

exitVar_decl(MiniCParser.Var_declContext ctx)

  • int x;, int x = 5;, int arr[10]; 같은 변수 선언을 변환.

exitType_spec(MiniCParser.Type_specContext ctx)

  • voidint 두 개의 자료형을 변환.

exitFun_decl(MiniCParser.Fun_declContext ctx)

  • 함수의 반환 타입, 이름, 매개변수, 본문을 조합하여 변환.
  • 예: int foo(int a, int b) { ... }

exitCompound_stmt(MiniCParser.Compound_stmtContext ctx)

  • {} 블록을 생성하며, 지역 변수 선언 후 개행을 추가하여 가독성 개선.

exitIf_stmt(MiniCParser.If_stmtContext ctx)

  • ifif-else를 변환하며, else 문 앞에 개행을 추가하여 가독성 향상.

exitWhile_stmt(MiniCParser.While_stmtContext ctx)

  • while 문 변환.

exitReturn_stmt(MiniCParser.Return_stmtContext ctx)

  • return 문 변환.

exitExpr(MiniCParser.ExprContext ctx)

  • 연산자, 함수 호출, 배열 접근 등의 표현식을 변환.

0개의 댓글