在符号 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
上进行操作)。
- 遇到新变量的声明:
运行 向上遍历整个堆栈以查看该变量是否已经存在。如果是,则发出错误 ('duplicate declaration/definition').
- 遇到访问变量:运行向上遍历整个堆栈以查看该变量是否存在;如果没有,发出一个错误('not declared/defined' – 我不会区分变量没有被定义或已经离开范围)。
- 离开作用域时,运行向上堆栈并删除驻留在该作用域中的所有变量。
为了能够做到 3. 你有(至少)两个选择:
- 每个堆栈条目都为各自的范围提供一个标识符,可以是简单的计数器。然后删除所有具有相同计数器值的变量。如果您担心计数器可能会溢出,那么也将其减 1(然后它始终代表当前范围深度)。
- 有一个哨兵类型——在打开一个新的范围时,它会被推入堆栈,比较不等于任何变量名,而在离开一个范围时,你会删除所有变量,直到遇到哨兵——和哨兵本身。
此处理会使您的 outOfScope
成员过时。
你的 addVariable
函数接受了太多参数,顺便说一下——为什么一个变量需要一个 return 类型,例如???
我建议为您的语言可能提供的每种特定语义类型添加多个函数 (addVariable
、addFunction
、...)。每个函数接受实际需要配置的内容,并将其余设置为适当的默认值(例如 addFunction
内的 Type
到 Function
)。
编辑 – 处理您评论中的示例:
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
我正在编写一个玩具编译器,它将 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
上进行操作)。
- 遇到新变量的声明:
运行 向上遍历整个堆栈以查看该变量是否已经存在。如果是,则发出错误 ('duplicate declaration/definition'). - 遇到访问变量:运行向上遍历整个堆栈以查看该变量是否存在;如果没有,发出一个错误('not declared/defined' – 我不会区分变量没有被定义或已经离开范围)。
- 离开作用域时,运行向上堆栈并删除驻留在该作用域中的所有变量。
为了能够做到 3. 你有(至少)两个选择:
- 每个堆栈条目都为各自的范围提供一个标识符,可以是简单的计数器。然后删除所有具有相同计数器值的变量。如果您担心计数器可能会溢出,那么也将其减 1(然后它始终代表当前范围深度)。
- 有一个哨兵类型——在打开一个新的范围时,它会被推入堆栈,比较不等于任何变量名,而在离开一个范围时,你会删除所有变量,直到遇到哨兵——和哨兵本身。
此处理会使您的 outOfScope
成员过时。
你的 addVariable
函数接受了太多参数,顺便说一下——为什么一个变量需要一个 return 类型,例如???
我建议为您的语言可能提供的每种特定语义类型添加多个函数 (addVariable
、addFunction
、...)。每个函数接受实际需要配置的内容,并将其余设置为适当的默认值(例如 addFunction
内的 Type
到 Function
)。
编辑 – 处理您评论中的示例:
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