代码之家  ›  专栏  ›  技术社区  ›  dangee1705

将赋值表达式编译为字节码[已关闭]

  •  2
  • dangee1705  · 技术社区  · 6 年前

    a + 2
    

    变成

    TOKEN_NAME
    TOKEN_ADD
    TOKEN_INT
    

    然后将其解析并转换为字节码,看起来像

    LOAD_VARIABLE (this is the a)
    LOAD_CONSTANT (this is the 2)
    ADD
    

    a[0][1] = 2
    

    会变成

    TOKEN_NAME
    TOKEN_L_BRACKET
    TOKEN_INT
    TOKEN_R_BRACKET
    TOKEN_L_BRACKET
    TOKEN_INT
    TOKEN_R_BRACKET
    TOKEN_ASSIGN
    TOKEN_INT
    

    我需要加载一个,在该对象上做一个下标(0下标),然后将2存储到1下标中。我要补充的是,解析器实际上是LL(1),这使得这一点特别困难。

    我想不出一种方法来确保左侧表达式的最后一部分(我要分配给它的部分)没有加载,而是将值(2)存储到其中。

    提前谢谢。

    1 回复  |  直到 6 年前
        1
  •  2
  •   chqrlie    6 年前

    可以使用简单的回溯方法构造引用:

    • 编译表达式,就像读取表达式一样
    • 如果下一个操作需要左值,请回顾上一个字节码操作
    • 根据下表转换最后一个操作:
      • LOAD_VALUE 转换为 GET_VALUE_REF
      • LOAD_PROPERTY 转换为 GET_PROPERTY_REF 加载属性 a.b )
      • LOAD_ELEMENT 转换为 GET_ELEMENT_REF 加载元件 a[b] )
      • 任何其他操作码都会生成无效的左值错误。

    这种方法对于最常见的语义来说已经足够了。对于C,您可以添加对解引用运算符的支持 * : GET_POINTER_VALUE GET_POINTER_REF 这基本上是禁止的。

    要实现这一点,您需要跟踪编译器生成的最后一个操作码,并有可能将其修补到另一个字节码中。

    表达 a[0][2] 将编译为

    LOAD_VARIABLE a (this is the a)
    LOAD_CONSTANT 0 (this is the 0)
    GET_ELEMENT
    LOAD_CONSTANT 2 (this is the 2)
    GET_ELEMENT
    

    a[0][2] = 3

    LOAD_VARIABLE a
    LOAD_CONSTANT 0
    GET_ELEMENT
    LOAD_CONSTANT 2
    GET_ELEMENT_REF
    LOAD_CONSTANT 3
    STORE_REF
    

    如果不需要引用(需要引用 a[b] += c

    a[0][2]=3

    LOAD_VARIABLE a
    LOAD_CONSTANT 0
    GET_ELEMENT
    LOAD_CONSTANT 2
    LOAD_CONSTANT 3
    STORE_ELEMENT (uses 3 stack slots)
    

    虽然 a[0][2] += 3

    LOAD_VARIABLE a
    LOAD_CONSTANT 0
    GET_ELEMENT
    LOAD_CONSTANT 2
    GET_ELEMENT_REF
    LOAD_REF
    LOAD_CONSTANT 3
    ADD
    STORE_REF