从零写一个编译器(十三):代码生成之遍历AST

项目的完整代码在 C2j-Compiler

前言

在上一篇完成对JVM指令的生成,下面就可以真正进入代码生成部分了。通常现代编译器都是先把生成IR,再经过代码优化等等,最后才编译成目标平台代码。但是时间水平有限,我们没有IR也没有代码优化,就直接利用AST生成Java字节码

入口

进行代码生成的入口在CodeGen,和之前解释器一样:先获取main函数的头节点,从这个节点开始,先进入函数定义,再进入代码块

函数定义节点

在进入函数定义节点的时候,就要生成一个函数定义对应的Java字节码,即一个静态方法(因为我们对整个C语言文件生成为一个类,main方法为public static main,其它的则是对应的静态方法,结构体则是另外的类)

  • 对于函数定义先从节点中拿到对应的函数命和参数
  • emitArgs是用来处理参数的,根据参数生成相应的Java字节码
  • 如果这个函数是main的话已经是交由前面处理了,逻辑差不多(具体在start.Start中)
case SyntaxProductionInit.NewName_LP_RP_TO_FunctDecl:
    root.reverseChildren();
    AstNode n = root.getChildren().get(0);
    String name = (String) n.getAttribute(NodeKey.TEXT);
    symbol = (Symbol) root.getAttribute(NodeKey.SYMBOL);
    generator.setCurrentFuncName(name);
    if (name != null && !name.equals("main")) {
        String declaration = name + emitArgs(symbol);
        generator.emitDirective(Directive.METHOD_PUBBLIC_STATIC, declaration);
        generator.setNameAndDeclaration(name, declaration);
    }
    copyChild(root, root.getChildren().get(0));
    break;

case SyntaxProductionInit.NewName_LP_VarList_RP_TO_FunctDecl:
    n = root.getChildren().get(0);
    name = (String) n.getAttribute(NodeKey.TEXT);
    symbol = (Symbol) root.getAttribute(NodeKey.SYMBOL);
    generator.setCurrentFuncName(name);
    if (name != null && !name.equals("main")) {
        String declaration = name + emitArgs(symbol);
        generator.emitDirective(Directive.METHOD_PUBBLIC_STATIC, declaration);
        generator.setNameAndDeclaration(name, declaration);
    }

    Symbol args = symbol.getArgList();

    if (args == null || argsList == null || argsList.isEmpty()) {
        System.err.println("generate function with arg list but arg list is null");
        System.exit(1);
    }
    break;

创建结构体和数组

数组

创建结构体和数组的节点在DefGenerate里,可以看到在这里只处理了数组和普通变量,有关结构体的处理是在对结构体第一次使用的时候。顺便提一下代码生成对于赋初值操作是没有进行处理的。

  • 如果是个数组,酒直接调用ProgramGenerator直接生成创建数组的指令
  • 如果是个普通变量,就直接找到它并且赋值为0(这里变量在队列里的位置是根据符号表来计算的,具体可以看上一篇的getLocalVariableIndex方法)
public class DefGenerate extends BaseGenerate {
    @Override
    public Object generate(AstNode root) {
        int production = (int) root.getAttribute(NodeKey.PRODUCTION);
        ProgramGenerator generator = ProgramGenerator.getInstance();
        Symbol symbol = (Symbol) root.getAttribute(NodeKey.SYMBOL);

        switch (production) {
            case SyntaxProductionInit.Specifiers_DeclList_Semi_TO_Def:
                Declarator declarator = symbol.getDeclarator(Declarator.ARRAY);
                if (declarator != null) {
                    if (symbol.getSpecifierByType(Specifier.STRUCTURE) == null) {
                        generator.createArray(symbol);
                    }
                } else {
                    int i = generator.getLocalVariableIndex(symbol);
                    generator.emit(Instruction.SIPUSH, "" + 0);
                    generator.emit(Instruction.ISTORE, "" + i);
                }

                break;

            default:
                break;
        }

        return root;
    }
}

结构体

处理结构体定义的代码在UnaryNodeGenerate,也就是只有在使用到结构体定义时才会进行定义

  • 先拿到当前UNARY的符号,如果instanceof ArrayValueSetter就说明是一个结构体数组,就进入getStructSymbolFromStructArray方法创建一个结构体数组,并返回当前下标的结构体对象
  • 设置当前结构体的作用域范围
  • 对结构体作为类进行定义
  • 然后对读取结构体的域
  • 其实可以忽略指针部分,因为代码生成并没有对指针进行模拟
case SyntaxProductionInit.Unary_StructOP_Name_TO_Unary:
    child = root.getChildren().get(0);
    String fieldName = (String) root.getAttribute(NodeKey.TEXT);
    Object object = child.getAttribute(NodeKey.SYMBOL);
    boolean isStructArray = false;

    if (object instanceof ArrayValueSetter) {
        symbol = getStructSymbolFromStructArray(object);
        symbol.addValueSetter(object);
        isStructArray = true;
    } else {
        symbol = (Symbol) child.getAttribute(NodeKey.SYMBOL);
    }

    if (isStructArray) {
        ArrayValueSetter vs = (ArrayValueSetter) object;
        Symbol structArray = vs.getSymbol();
        structArray.addScope(ProgramGenerator.getInstance().getCurrentFuncName());
    } else {
        symbol.addScope(ProgramGenerator.getInstance().getCurrentFuncName());
    }

    ProgramGenerator.getInstance().putStructToClassDeclaration(symbol);

    if (isSymbolStructPointer(symbol)) {
        copyBetweenStructAndMem(symbol, false);
    }

    Symbol args = symbol.getArgList();
    while (args != null) {
        if (args.getName().equals(fieldName)) {
            args.setStructParent(symbol);
            break;
        }

        args = args.getNextSymbol();
    }

    if (args == null) {
        System.err.println("access a filed not in struct object!");
        System.exit(1);
    }

    if (args.getValue() != null) {
        ProgramGenerator.getInstance().readValueFromStructMember(symbol, args);
    }

    root.setAttribute(NodeKey.SYMBOL, args);
    root.setAttribute(NodeKey.VALUE, args.getValue());

    if (isSymbolStructPointer(symbol)) {
        checkValidPointer(symbol);
        structObjSymbol = symbol;
        monitorSymbol = args;

        GenerateBrocasterImpl.getInstance().registerReceiverForAfterExe(this);
    } else {
        structObjSymbol = null;
    }
    break;

一元操作节点

这个节点和在解释器的有很多相同,除了有对结构体的操作,其它的也是有非常重要的作用

  • 像数字、字符串或者是变量和之前的操作都是把信息传递到父节点,交由父节点处理
case SyntaxProductionInit.Number_TO_Unary:
    text = (String) root.getAttribute(NodeKey.TEXT);
    boolean isFloat = text.indexOf('.') != -1;
    if (isFloat) {
        value = Float.valueOf(text);
        root.setAttribute(NodeKey.VALUE, value);
    } else {
        value = Integer.valueOf(text);
        root.setAttribute(NodeKey.VALUE, value);
    }
    break;

case SyntaxProductionInit.Name_TO_Unary:
    symbol = (Symbol) root.getAttribute(NodeKey.SYMBOL);
    if (symbol != null) {
        root.setAttribute(NodeKey.VALUE, symbol.getValue());
        root.setAttribute(NodeKey.TEXT, symbol.getName());
    }
    break;

case SyntaxProductionInit.String_TO_Unary:
    text = (String) root.getAttribute(NodeKey.TEXT);
    root.setAttribute(NodeKey.VALUE, text);
    break;

case SyntaxProductionInit.Unary_LB_Expr_RB_TO_Unary:
    child = root.getChildren().get(0);
    symbol = (Symbol) child.getAttribute(NodeKey.SYMBOL);

    child = root.getChildren().get(1);
    int index = 0;
    if (child.getAttribute(NodeKey.VALUE) != null) {
        index = (Integer) child.getAttribute(NodeKey.VALUE);
    }
    Object idxObj = child.getAttribute(NodeKey.SYMBOL);

    try {
        Declarator declarator = symbol.getDeclarator(Declarator.ARRAY);
        if (declarator != null) {
            Object val = declarator.getElement((int) index);
            root.setAttribute(NodeKey.VALUE, val);
            ArrayValueSetter setter;
            if (idxObj == null) {
                setter = new ArrayValueSetter(symbol, index);
            } else {
                setter = new ArrayValueSetter(symbol, idxObj);
            }

            root.setAttribute(NodeKey.SYMBOL, setter);
            root.setAttribute(NodeKey.TEXT, symbol.getName());

        }
        Declarator pointer = symbol.getDeclarator(Declarator.POINTER);
        if (pointer != null) {
            setPointerValue(root, symbol, index);
            PointerValueSetter pv = new PointerValueSetter(symbol, index);
            root.setAttribute(NodeKey.SYMBOL, pv);
            root.setAttribute(NodeKey.TEXT, symbol.getName());
        }
    } catch (Exception e) {
        e.printStackTrace();
        System.exit(1);
    }
    break;

赋值操作

  • 如果当前是一个数组,先拿到它的符号和下标
  • 如果不是结构体数组,那么拿到下标直接用readArrayElement生成读取数组元素的指令
  • 如果是一个符号则用getLocalVariableIndex读取这个符号的值
  • 如果是一个常数,则直接生成IPUSH指令
  • 最后进行赋值操作,如果不是对结构体的域进行赋值就直接用getLocalVariableIndex拿到队列位置然后生成ISTORE
  • 如果是对结构体数组的元素的域的赋值,就调用assignValueToStructMemberFromArray生成代码,如果只是结构体就直接调用assignValueToStructMember生成代码
ProgramGenerator generator = ProgramGenerator.getInstance();

if (BaseGenerate.resultOnStack) {
    this.value = obj;
    BaseGenerate.resultOnStack = false;
} else if (obj instanceof ArrayValueSetter) {
    ArrayValueSetter setter = (ArrayValueSetter) obj;
    Symbol symbol = setter.getSymbol();
    Object index = setter.getIndex();
    if (symbol.getSpecifierByType(Specifier.STRUCTURE) == null) {
        if (index instanceof Symbol) {
            ProgramGenerator.getInstance().readArrayElement(symbol, index);
            if (((Symbol) index).getValue() != null) {
                int i = (int) ((Symbol) index).getValue();
                try {
                    this.value = symbol.getDeclarator(Declarator.ARRAY).getElement(i);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } else {
            int i = (int) index;
            try {
                this.value = symbol.getDeclarator(Declarator.ARRAY).getElement(i);
            } catch (Exception e) {
                e.printStackTrace();
            }

            ProgramGenerator.getInstance().readArrayElement(symbol, index);
        }
    }
} else if (obj instanceof Symbol) {
    Symbol symbol = (Symbol) obj;
    this.value = symbol.value;
    int i = generator.getLocalVariableIndex(symbol);
    generator.emit(Instruction.ILOAD, "" + i);
} else if (obj instanceof Integer) {
    Integer val = (Integer) obj;
    generator.emit(Instruction.SIPUSH, "" + val);
    this.value = obj;
}

if (!this.isStructMember()) {
    int idx = generator.getLocalVariableIndex(this);
    if (!generator.isPassingArguments()) {
        generator.emit(Instruction.ISTORE, "" + idx);
    }
} else {
    if (this.getStructSymbol().getValueSetter() != null) {
        generator.assignValueToStructMemberFromArray(this.getStructSymbol().getValueSetter(), this, this.value);
    } else {
        generator.assignValueToStructMember(this.getStructSymbol(), this, this.value);
    }
}

最后

完成这部分后,对下面的代码

void quicksort(int A[10], int p, int r) {
    int x;
    int i;
    i = p - 1;
    int j;
    int t;
    int v;
    v = r - 1;
    if (p < r) {
        x = A[r];
        for (j = p; j <= v; j++) {
            if (A[j] <= x) {
                i++;
                t = A[i];
                A[i] = A[j];
                A[j] = t;
            }
        }
        v = i + 1;
        t = A[v];
        A[v] = A[r];
        A[r] = t;
        t = v - 1;
        quicksort(A, p, t);
        t = v + 1;
        quicksort(A, t, r);
    }
}

void main () {
    int a[10];
    int i;
    int t;
    printf("before quick sort:");
    for(i = 0; i < 10; i++) {
        t = (10 - i);
        a[i] = t;
        printf("value of a[%d] is %d", i, a[i]);
    }
    quicksort(a, 0, 9);
    printf("after quick sort:");
    for (i = 0; i < 10; i++) {
        printf("value of a[%d] is %d", i, a[i]);
    }
}

则会生成下面的Java字节码

.class public C2Bytecode
.super java/lang/Object

.method public static main([Ljava/lang/String;)V
    sipush  10
    newarray    int
    astore  0
    sipush  0
    istore  1
    sipush  0
    istore  2
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    ldc "before quick sort:"
    invokevirtual   java/io/PrintStream/print(Ljava/lang/String;)V
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    ldc "
"
    invokevirtual   java/io/PrintStream/print(Ljava/lang/String;)V
    sipush  0
    istore  1

loop0:
    iload   1
    sipush  10
if_icmpge branch0
    sipush  10
    iload   1
    isub
    istore  2
    aload   0
    iload   1
    iload   2
    iastore
    aload   0
    iload   1
    iaload
    istore  3
    iload   1
    istore  4
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    ldc "value of a["
    invokevirtual   java/io/PrintStream/print(Ljava/lang/String;)V
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    iload   4
    invokevirtual   java/io/PrintStream/print(I)V
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    ldc "] is "
    invokevirtual   java/io/PrintStream/print(Ljava/lang/String;)V
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    iload   3
    invokevirtual   java/io/PrintStream/print(I)V
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    ldc "
"
    invokevirtual   java/io/PrintStream/print(Ljava/lang/String;)V
    iload   1
    sipush  1
    iadd
    istore  1
goto loop0
branch0:
    aload   0
    sipush  0
    sipush  9
    invokestatic    C2Bytecode/quicksort([III)V
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    ldc "after quick sort:"
    invokevirtual   java/io/PrintStream/print(Ljava/lang/String;)V
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    ldc "
"
    invokevirtual   java/io/PrintStream/print(Ljava/lang/String;)V
    sipush  0
    istore  1

loop2:
    iload   1
    sipush  10
if_icmpge branch4
    aload   0
    iload   1
    iaload
    istore  3
    iload   1
    istore  4
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    ldc "value of a["
    invokevirtual   java/io/PrintStream/print(Ljava/lang/String;)V
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    iload   4
    invokevirtual   java/io/PrintStream/print(I)V
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    ldc "] is "
    invokevirtual   java/io/PrintStream/print(Ljava/lang/String;)V
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    iload   3
    invokevirtual   java/io/PrintStream/print(I)V
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    ldc "
"
    invokevirtual   java/io/PrintStream/print(Ljava/lang/String;)V
    iload   1
    sipush  1
    iadd
    istore  1
goto loop2
branch4:
    return
.end method
.method public static quicksort([III)V
    sipush  2
    newarray    int
    astore  6
    sipush  0
    istore  5
    sipush  1
    istore  5
    aload   6
    iload   5
    sipush  1
    iastore
    aload   6
    sipush  1
    iaload
    istore  10
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    ldc "before quick sort: "
    invokevirtual   java/io/PrintStream/print(Ljava/lang/String;)V
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    iload   10
    invokevirtual   java/io/PrintStream/print(I)V
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    ldc "
"
    invokevirtual   java/io/PrintStream/print(Ljava/lang/String;)V
    sipush  0
    istore  9
    sipush  0
    istore  3
    iload   1
    sipush  1
    isub
    istore  3
    sipush  0
    istore  4
    sipush  0
    istore  7
    sipush  0
    istore  8
    iload   2
    sipush  1
    isub
    istore  8
    iload   1
    iload   2
if_icmpge branch1

    aload   0
    iload   2
    iaload
    istore  9
    iload   1
    istore  4

loop1:

    iload   4
    iload   8
if_icmpgt ibranch1

    aload   0
    iload   4
    iaload
    iload   9
if_icmpgt ibranch2

    iload   3
    sipush  1
    iadd
    istore  3
    aload   0
    iload   3
    iaload
    istore  7
    aload   0
    iload   3
    aload   0
    iload   4
    iaload
    iastore
    aload   0
    iload   4
    iload   7
    iastore
ibranch2:

    iload   4
    sipush  1
    iadd
    istore  4
goto loop1

ibranch1:

    iload   3
    sipush  1
    iadd
    istore  8
    aload   0
    iload   8
    iaload
    istore  7
    aload   0
    iload   8
    aload   0
    iload   2
    iaload
    iastore
    aload   0
    iload   2
    iload   7
    iastore
    iload   8
    sipush  1
    isub
    istore  7
    aload   0
    iload   1
    iload   7
    invokestatic    C2Bytecode/quicksort([III)V
    iload   8
    sipush  1
    iadd
    istore  7
    aload   0
    iload   7
    iload   2
    invokestatic    C2Bytecode/quicksort([III)V
branch1:

    return
.end method

.end class

小结

这篇的代码生成和之前解释器的思路很相似,都是根据AST和对应的产生式来执行或者生成代码。

其实主要的思路是很清晰的,只是其中有太多细节容易让人太过纠结。这个系列算作是我自己的学习笔记,到这也有十三篇了,下一篇可能写写总结就正式结束了。

欢迎Star!

原文地址:https://www.cnblogs.com/secoding/p/11391239.html

时间: 2024-11-07 14:41:29

从零写一个编译器(十三):代码生成之遍历AST的相关文章

从零写一个编译器(三):语法分析之几个基础数据结构

项目的完整代码在 C2j-Compiler 写在前面 这个系列算作为我自己在学习写一个编译器的过程的一些记录,算法之类的都没有记录原理性的东西,想知道原理的在龙书里都写得非常清楚,但是我自己一开始是不怎么看得下来,到现在都还没有完整的看完,它像是一本给已经有基础的人写的书. 在parse包里一共有8个文件,就是语法分析阶段写的所有东西啦 Symbols.java Production.java SyntaxProductionInit.java FirstSetBuilder.java Prod

从零写一个编译器(十):编译前传之直接解释执行

项目的完整代码在 C2j-Compiler 前言 这一篇不看也不会影响后面代码生成部分 现在经过词法分析语法分析语义分析,终于可以进入最核心的部分了.前面那部分可以称作编译器的前端,代码生成代码优化都是属于编译器后端,如今有关编译器的工作岗位主要都是对后端的研究.当然现在写的这个编译器因为水平有限,并没有优化部分. 在进行代码生成部分之前,我们先来根据AST来直接解释执行,其实就是对AST的遍历.现代解释器一般都是生成一个比较低级的指令然后跑在虚拟机上,但是简单起见我们就直接根据AST解释执行的

从零写一个编译器(十一):代码生成之Java字节码基础

项目的完整代码在 C2j-Compiler 前言 第十一篇,终于要进入代码生成部分了,但是但是在此之前,因为我们要做的是C语言到字节码的编译,所以自然要了解一些字节码,但是由于C语言比较简单,所以只需要了解一些字节码基础 JVM的基本机制 JVM有一个执行环境叫做stack frame 这个环境有两个基本数据结构 执行堆栈:指令的执行,都会围绕这个堆栈来进行 局部变量数组,参数和局部变量就存储在这个数组. 还有一个PC指针,它指向下一条要执行的指令. 举一个例子 int f(int a, int

从零写一个编译器(七):语义分析之符号表的数据结构

项目的完整代码在 C2j-Compiler 前言 有关符号表的文件都在symboltable包里 前面我们通过完成一个LALR(1)有限状态自动机和一个reduce信息来构建了一个语法解析表,正式完成了C语言的语法解析.接下来就是进入语义分析部分,和在第二篇提到的一样,语义分析的主要任务就是生成符号表来记录变量和变量的类型,并且发现不符合语义的语句 描述变量 在C语言里对变量声明定义里,主要有两种描述 说明符(Specifier) 说明符也就是对应C语言的一些描述变量类型或者像static,ex

从零写一个编译器(五):语法分析之自动机的缺陷和改进

项目的完整代码在 C2j-Compiler 前言 在上一篇,已经成功的构建了有限状态自动机,但是这个自动机还存在两个问题: 无法处理shift/reduce矛盾 状态节点太多,导致自动机过大,效率较低 这一节就要解决这两个问题 shift/reduce矛盾 看上一节那个例子的一个节点 e -> t . t -> t . * f 这时候通过状态节点0输入t跳转到这个节点,但是这时候状态机无法分清是根据推导式1做reduce还是根据推导式2做shift操作,这种情况就称之为shift / redu

从零写一个编译器(六):语法分析之表驱动语法分析

项目的完整代码在 C2j-Compiler 前言 上一篇已经正式的完成了有限状态自动机的构建和足够判断reduce的信息,接下来的任务就是根据这个有限状态自动机来完成语法分析表和根据这个表来实现语法分析 reduce信息 在完成语法分析表之前,还差最后一个任务,那就是描述reduce信息,来指导自动机是否该进行reduce操作 reduce信息在ProductionsStateNode各自的节点里完成,只要遍历节点里的产生式,如果符号"."位于表达式的末尾,那么该节点即可根据该表达式以

从零写一个编译器(四):语法分析之构造有限状态自动机

项目的完整代码在 C2j-Compiler 通过上一篇对几个构造自动机的基础数据结构的描述,现在就可以正式来构造有限状态自动机 我们先用一个小一点的语法推导式来描述这个过程 s -> e e -> e + t e -> t t -> t * f t -> f f -> ( e ) f -> NUM 初始化 状态0是状态机的初始状态,它包含着语法表达式中的起始表达式,也就是编号为0的表达式: 0: s -> . e 这里的点也就是之前Production类中的

从零写一个编译器(二):语法分析之前置知识

前言 在之前完成了词法分析之后,得到了Token流,那么接下来就是实现语法分析器来输入Token流得到抽象语法树 (Abstract Syntax Tree,AST).但是在完成这个语法分析器不像词法分析器,直接手撸就好了,还是需要一些前置的知识. 这些前置知识在之前的博文都有提起过 之前的博文目录 项目的完整代码在 C2j-Compiler 什么是语法分析? 如果我们把词法分析看成是组合单词,输出单词流,那么语法分析就可以看作是检查这些单词是不是符合语法的过程.在词法分析的时候用正则或者手工比

学了编译原理能否用 Java 写一个编译器或解释器?

16 个回答 默认排序? RednaxelaFX JavaScript.编译原理.编程 等 7 个话题的优秀回答者 282 人赞同了该回答 能.我一开始学编译原理的时候就是用Java写了好多小编译器和解释器.其实用什么语言来实现编译器并不是最重要的部分(虽然Java也不是实现编译器最方便的语言),最初用啥语言都可以. 我在大学的时候,我们的软件工程和计算机科学的编译原理课的作业好像都是可以用Java来写的.反正我印象中我给这两门课写的作业都是用的Java. ===================