在符号 table 中如何标记变量超出范围?

In symbol table how to mark variable out of scope?

我正在编写一个玩具编译器,它将 c/c++ 类语言编译为 c++。 我正在使用野牛,但是当变量超出范围时很难处理这种结构。

源码中整个main函数中只能有一个同名变量,很好,但是有个问题我解决不了

我不能像这样编写 C++ 代码,因为 C++ 编译器会抛出语义错误: 'var' 未在此范围内声明。

    int main()
    {
        if (true)
        {
            int var = 4;
        }
        if (true)
        {
            var = 5;
        }
    }

源语言有 while、if 和 if/else 语句。 如果声明的变量超出范围,我必须抛出语义错误。 例如:

这应该是语义错误,所以我无法生成这段代码:

int main()
{
    while(0)
    {
        int var = 1;
    }

    if (1)
    {
        var = 2;
    }
}

这也一定是语义错误:

int main()
{
    if (0)
    {
        int var = 1;
    }
    else
    {
        if (1)
        {
            var = 5;
        }
    }
}

这是允许的,我可以生成这段代码:

int main()
{
    if (0)
    {

    }
    else
    {
        int var = 1;
        if (1)
        {
            while (0)
            {
                while (0)
                {
                    var = 2;
                }
            }
        }
    }
}

我尝试了很多东西,但是当有嵌套的if、if/else或while时我无法解决。

我阅读了大量关于符号 table 的教程,但其中 none 可以正确解释如何管理超出范围的变量。

如果你熟悉这个话题和野牛,请不要只是给我提示,比如“使用堆栈,如果变量超出范围就标记它”。我发现了很多关于它的文章。 请给我伪代码或具体的实现草图。

我觉得应该不会有那么难,因为整个main函数中可以有一个和我写的同名的变量

符号table:

struct SymbolType
{
    int lineNumber;
    std::string identifier;
    int identifierValue;
    Type type;
    int functionArgumentNumber;
    Type functionReturnType;
    Type markType;
    bool outOfScope;
};

    class Symbol
    {
        public:
            void AddVariable(int _lineNumber, std::string _identifier, int _identifierValue, Type _type, int _functionArgumentNumber, Type _functionReturnType, Type _markType, bool _outOfScope);
            void AddMarker(int _lineNumber, std::string _scopeName, Type _markType);
            bool FindVariable(std::string _identifier);
            int FindVariableValue(std::string _identifier);
            void Remove();
            void Print();
            std::vector<SymbolType> symbolTable;

        private:
            int lineNumber;
            std::string identifier;
            int identifierValue;
            Type type;
            int functionArgumentNumber;
            Type functionReturnType;
            Type markType;
            bool outOfScope;
    };

现在假设如下:当您处于嵌套范围内时,您无法将变量添加到父范围。所以我们可以工作,例如具有类似堆栈的结构(最后的 push/pop 就足够了,但可以读取所有条目 - 后者的要求取消了 std::stack 的资格,因此我们将在 std::vector 上进行操作)。

  1. 遇到新变量的声明:
    运行 向上遍历整个堆栈以查看该变量是否已经存在。如果是,则发出错误 ('duplicate declaration/definition').
  2. 遇到访问变量:运行向上遍历整个堆栈以查看该变量是否存在;如果没有,发出一个错误('not declared/defined' – 我不会区分变量没有被定义或已经离开范围)。
  3. 离开作用域时,运行向上堆栈并删除驻留在该作用域中的所有变量。

为了能够做到 3. 你有(至少)两个选择:

  1. 每个堆栈条目都为各自的范围提供一个标识符,可以是简单的计数器。然后删除所有具有相同计数器值的变量。如果您担心计数器可能会溢出,那么也将其减 1(然后它始终代表当前范围深度)。
  2. 有一个哨兵类型——在打开一个新的范围时,它会被推入堆栈,比较不等于任何变量名,而在离开一个范围时,你会删除所有变量,直到遇到哨兵——和哨兵本身。

此处理会使您的 outOfScope 成员过时。

你的 addVariable 函数接受了太多参数,顺便说一下——为什么一个变量需要一个 return 类型,例如???

我建议为您的语言可能提供的每种特定语义类型添加多个函数 (addVariableaddFunction、...)。每个函数接受实际需要配置的内容,并将其余设置为适当的默认值(例如 addFunction 内的 TypeFunction)。

编辑 – 处理您评论中的示例:

while(condition)
{   // opens a new scope:
    // either place a sentinel or increment the counter (scope depth)

    int var = 1; // push an entry onto the stack
                 // (with current counter value, if not using sentinels)

    if (true)
    {   // again a new scope, see above

        var = 2; // finds 'var' on the stack, so fine

    }   // closes a scope:
        // either you find immediately the sentinel, pop it from the stack
        // and are done
        //
        // or you discover 'var' having a counter value less than current
        // counter so you are done as well

}   // closing next scope:
    // either you find 'var', pop it from the stack, then the sentinel,
    // pop it as well and are done
    //
    // or you discover 'var' having same counter value as current one,
    // so pop it, then next variable has lower counter value again or the
    // stack is empty, thus you decrement the counter and are done again