五种语句的综合,继承属性流动

这使得“参数传递”(属性流动)的讨论更加丰富。我们现在有两种清晰的“参数”流:

  1. 综合属性 (Synthesized Attribute) [自底向上]

    • 是什么: 这就是您函数返回的 node 对象。
    • 目的: 构建AST。子节点(如 translateASTFactor)构建一个小树,并通过 return 语句将其“传递”给父节点(如 translateASTTerm),父节点再将其组合成一个更大的树。
    • 方向: Bottom-Up (自底向上)
  2. 继承属性 (Inherited Attribute) [自顶向下]

    • 是什么: 这就是您通过函数参数(如 const std::string& type)传递的信息。
    • 目的: 提供上下文。父节点(如 translateASTDeclareStatement)将上下文信息(“我们正在声明的变量是 int 类型”)传递下去,子节点(如 translateASTVarList)使用这个信息来执行语义动作(如更新符号表 idenTable)。
    • 方向: Top-Down (自顶向下)

下面,我们使用您 C++ 代码中的文法,重新详细讲解这五种语句的属性流动。


1. 声明语句 (Declaration Statement)

  • C++ 代码: translateASTDeclareStatement, translateASTVarList(type), translateASTB(type)

  • 文法: <变量说明语句> -> 变量说明 <标识符列表>

  • AST 目标: (declare [Type] (varList [ID1] (linkVarList [ID2] ...)))

  • 属性流 (最完整的例子):

    这是一个同时展现了继承综合属性的完美例子。

    1. Top-Down (继承属性):

      • translateASTDeclareStatement 首先读取 “变量说明” (例如 int),并将其存储在 std::string type 中。
      • 向下调用 translateASTVarList(type),将 type 字符串作为继承属性传递下去。
      • translateASTVarList 接着向下调用 translateASTB(type),继续将这个 type 属性传递下去。
    2. 语义动作 (使用继承属性):

      • translateASTVarListtranslateASTB 内部,当它们 match("标识符") (例如 a) 时,它们会立即使用这个继承来的 type 属性来更新符号表 (Context)
        • idenTable.Add(name)
        • idenTable.UpdateTypeByName(name, type)
    3. Bottom-Up (综合属性):

      • translateASTB 构建并返回 (linkVarList ...) 节点 (作为 B.ast)。
      • translateASTVarList 接收这个 B.ast,将它和 idNode.ast 组合成 (varList ...) 节点,并返回这个节点 (作为 varList.ast)。
      • translateASTDeclareStatement 接收这个 varList.ast (存入 node varList),将它和 typeNode.ast 组合成 (declare ...) 节点,并返回这个最终的AST子树 (作为 <变量说明语句>.ast)。
  • 代码中的“综合”与“继承”:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // P.S. 这是对您代码的简化伪代码,以突出属性流
    node translateASTDeclareStatement()
    {
    node root = new node("declare");
    string type = currentToken.value; // 1. 获取上下文
    node typeNode(type);

    // 2. 将 'type' 作为继承属性(inh)向下传递
    node c2 = translateASTVarList(type);
    // c2 是 translateASTVarList 返回的综合属性(ast)

    // 3. 组合 (Synthesize)
    root.addChild(typeNode); // c1.ast
    root.addChild(c2); // c2.ast

    // 4. 将组合后的新树作为自己的综合属性(ast)返回
    return root;
    }

2. 赋值语句 (Assignment Statement)

  • C++ 代码: translateASTAssignmentStatement()

  • 文法: <赋值语句> -> 标识符 赋值号 <表达式>

  • AST 目标: (assign [ID] [Expression])

  • 属性流 (自底向上 + 语义检查):

    1. Bottom-Up (综合属性):

      • 这是 “结构化映射” 的经典例子。
      • translateASTAssignmentStatement 被调用。
      • 它创建 idNode (例如 a)。
      • 向下调用 translateASTExpression()
      • translateASTExpression 经过一系列复杂的递归,最终返回一个完整的表达式子树(例如 (add b c))。这就是 <表达式>.ast 综合属性。
      • translateASTAssignmentStatement 接收这个子树 (存入 node expression)。
      • 综合: 它将 idNodeexpression 挂在一个新根 root (“assign”) 下。
      • 返回: root (即 <赋值语句>.ast) 被返回
    2. 语义动作 (使用Context):

      • 组合 AST 之前,它会检查上下文(idenTable)以验证语义:
      • if (!idenTable.identifierExists(name))
      • 这是一个在AST构建时发生的即时语义检查,它利用 idenTable(符号表)作为上下文,而不是通过参数传递。

3. 条件语句 (Conditional Statement)

  • C++ 代码: translateASTConditionalStatement()

  • 文法: ... if (<表达式>) <嵌套语句> else <嵌套语句>

  • AST 目标: (ifThenElse [Condition] [Then-Branch] [Else-Branch])

  • 属性流 (纯自底向上):

    1. Bottom-Up (综合属性):
      • 这是最纯粹的“填空”式综合
      • 它创建 condStmtNode("ifThenElse")
      • 调用 1: translateASTExpression() 被调用,它返回 <表达式>.ast (存入 node expression)。
      • 调用 2: translateASTNestedStatement() 被调用,它返回 <嵌套语句>_1.ast (存入 nestedStmt1)。
      • 调用 3: translateASTNestedStatement() 被调用,它返回 <嵌套语句>_2.ast (存入 nestedStmt2)。
      • 综合: 它将这三个已完成的子树作为 ifThenElse 节点的子节点。
      • 返回: condStmtNode (即 <条件语句>.ast) 被返回
  • 特点: 结构固定,没有歧义,只是简单地收集子节点返回的 .ast 属性并组装。


4. 循环语句 (Loop Statement)

  • C++ 代码: translateASTLoopStatement()

  • 文法: while (<表达式>) : <嵌套语句>

  • AST 目标: (while [Condition] [Body])

  • 属性流 (纯自底向上):

    • if 语句完全相同,只是子节点从三个变成了两个。
    • 调用 1: translateASTExpression() 被调用,返回 <表达式>.ast (存入 node expression)。
    • 调用 2: translateASTNestedStatement() 被调用,返回 <嵌套语句>.ast (存入 nestedStmt)。
    • 综合:expressionnestedStmt 挂在新根 root (“while”) 下。
    • 返回: root (即 <循环语句>.ast) 被返回

5. 表达式 (Expression)

  • C++ 代码: translateASTExpression(), translateASTH(), translateASTI(), translateASTD(), translateASTG(), translateASTE(), …

  • 文法: (一套处理 or, and, not, rel, +, -, *, / 的复杂文法)

  • AST 目标: (add (mul a b) c) (对于 a * b + c,需要正确的优先级和左结合)

  • 属性流 (自底向上 + 重构):

    您的 C++ 代码在处理表达式时,完美地结合了我们之前讨论的两个算法:

    1. 算法1:用函数调用栈处理“优先级” (综合流)

      • translateASTExpression (处理 or)
      • -> translateASTConjunction (处理 and)
      • -> translateASTInversion (处理 not)
      • -> translateASTRelationExpression (处理 rel)
      • -> translateASTMathExpression (处理 +, -)
      • -> translateASTTerm (处理 *, /)
      • -> translateASTFactor (处理 ID, 整数, ( ))
      • 这个调用链本身就编码了运算符的优先级。Factor(优先级最高)在栈的最深处被解析。它返回 (综合) 它的 node.ast,然后 Term 处理它,再返回,依此类推。
    2. 算法2:用树重构处理“左结合性” (综合流)

      • 这仍然是代码中最精妙的部分,它处理 a + b + c 这样的左结合链。
      • translateASTH (处理 +, -), translateASTI (处理 *, /), translateASTD (处理 or), translateASTG (处理 and) 全都使用了这个算法
      • 追踪 a + b + c (在 translateASTMathExpression 中):
        1. 它调用 translateASTTerm() 得到 a (返回 c1 = (term a ...) )。
        2. 它调用 translateASTH() 来处理 + b + c (返回 c2)。
        3. 深入 translateASTH() (+ b + c):
          • op = "add", c1_H = (term b ...) (通过调用 translateASTTerm 获得)。
          • 递归调用 translateASTH() (+ c):
            • op = "add", c1_H_inner = (term c ...)
            • 递归调用 translateASTH() (空) -> 返回 (综合) (empty)
            • c2empty。它创建并返回 (综合) (add (term c ...))
          • c2_H 现在是 (add (term c ...))c2_H.root != "empty"
          • newFirstChild = (add (term b ...))
          • c2_H.getFirstLeftNonLeafChild(insertPoint) 返回 false (因为 c 是叶子)。
          • c2_H.addNodeAsFirstChild(newFirstChild)c2_H 变为 (add (add (term b ...)) (term c ...))
          • translateASTH 返回 (综合) 这个 (add (add ...)) 树。
        4. 回到 translateASTMathExpression:
          • c1 = (term a ...)
          • c2 = (add (add (term b ...)) (term c ...))
          • c2.root != "empty"
          • c2.getFirstLeftNonLeafChild(insertPoint) 返回 trueinsertPoint 现在指向 (add (term b ...)) 节点。
          • insertPoint.addNodeAsFirstChild(c1)
          • c2 (完整的树) 变为 (add (add (term a ...)) (term b ...)) (term c ...)) (这里假设 add 可以有多个孩子,或者 getFirstLeft... 找到了正确的 add 节点)。
          • 等等,让我重读您的 getFirstLeftNonLeafChild C# 逻辑…
          • (
            pointer = children[0];
            while(pointer.children.Count != 0) { lastPointer = pointer; pointer = pointer.children[0]; }
            return lastPointer;
            )
          • OK,C# 逻辑追踪:
          • c2 = (add (add (term b)) (term c)) (简化版)
          • insertPoint = c2.getFirstLeft...():
            • pointer = (add (term b))
            • while: lastPointer = (add (term b)), pointer = (term b)
            • while: pointer.children.Count == 0 -> 循环终止
            • 返回 lastPointer,即 (add (term b)) 节点。
          • insertPoint.addNodeAsFirstChild(c1) (即 (term a))
          • insertPoint 变为 (add (term a) (term b))
          • c2 (完整的树) 变为 (add (add (term a) (term b)) (term c))
        5. 结论: 您的 C++ 代码完美地复现了 C# 中的左结合树重构算法。它仍然是一个纯粹的综合属性 (Bottom-Up) 流,但父节点不是简单地组合子节点,而是重构子节点返回的树,以确保正确的结合性。

总结对比

语句类型 C++ 代码 属性流 (参数传递)
声明语句 translateASTDeclareStatement (1) 继承 (Top-Down): const string& type 被传递下去。
(2) 综合 (Bottom-Up): node 对象被 return 回来。
赋值语句 translateASTAssignmentStatement 综合 (Bottom-Up): node 对象被 return 回来。
条件语句 translateASTConditionalStatement 综合 (Bottom-Up): 纯粹的结构化组合。if 节点收集三个 return 回来的 node
循环语句 translateASTLoopStatement 综合 (Bottom-Up): 纯粹的结构化组合。while 节点收集两个 return 回来的 node
表达式 translateASTH, translateASTI, … 综合 (Bottom-Up) + 重构: node 对象被 return,但父节点会修改子树的结构 (使用 getFirst...) 来保证左结合性。