微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用二元和一元节点处理解析器?

如何解决使用二元和一元节点处理解析器?

我现在这标题有点奇怪,但我希望你能理解我在问什么。几个月前,我在 Python 中开发了 Interpreter 程序,这很棒,但现在我想在 C++ 中实现相同的程序,但这样做给我带来了很大的问题,因为 C++ 是类型严格的。

让我们从我在 python 程序中所做的开始。首先,我创建了一个词法分析器,它将所有内容分成标记(键值对),然后我编写了一个解析器,它将算术语法转换为操作节点,如 BinaryOpNode、UnaryOpNode 和 NumberNode。 ex- (-2+7)^3 将被转换为 AST 作为一个二元节点,左节点为另一个二元节点,算子为 POW(power),右边节点为 3 的数字节点。该节点的左节点为二元节点其左节点是一元节点(MINUS 和一个数字节点 2),运算符为 PLUS,右节点为数字节点 7。

我通过识别表达式、术语和因子来做到这一点。我用 C++ 编写了一个词法分析器,但在解析器中遇到了问题。请帮我用 C++ 做同样的事情。

到目前为止我做了什么??

我尝试了一些奇怪但有效的方法。我创建了一个 BinaryOpNode 类,其中有两个 void* 成员用于右节点和左节点,一个 Enum 成员用于在 Rt 和 Lt 节点之间进行操作。现在,两个节点的另外两个布尔成员将有助于了解现在的 void* Lt 和 Rt 类型是什么?它们是 UnaryOpNode 还是 BinaryOpNode(认)。这将帮助我将 Node 类型转换为相应的类型。

但是,我对结果并不满意,因为它们看起来优化程度较低,而且我无法以这种方式跟踪 NumberNode。

请帮帮我。提前致谢

解决方法

您正在寻找的是多态性。也就是说,程序员编写的代码,根据它所操作的事物的类型来做不同的事情。

C++ 支持一系列令人眼花缭乱的多态方法。

最受支持的类型是基于继承的虚拟多态。在这里,您创建一个基类:

struct INode {
  virtual ~INode() {}
};

并向其添加常用操作,使这些常用操作成为纯虚拟操作:

struct INode {
  virtual ~INode() {}
  virtual std::vector<INode*> GetChildren() const = 0;
};

这要求您使用指针而不是对象实例。

在这个系统中,如果你知道一个对象的类型,你可以使用dynamic_cast<RealType*>(iNodePointer)来获取一个指向该对象的指针作为该类型的实例。如果类型不匹配,则返回 nullptr。这使您可以访问您在继承类型中拥有但不在基本接口中的方法。


第二种多态性是基于 std::variant 的。这是一组封闭的类型,解析器经常使用。

using AnyNode = std::variant<Node::BinaryOp,Node::UnaryOp,Node::Number>;

这里使用 std::visit 来操作具体类型而不是 dynamic_cast,并且您的解析树是基于值的而不是基于指针的。

当您希望一个节点在其内部具有一个 AnyNode 向量时会有些麻烦。


第三种方式是 std::function 类型擦除样式。在这里,您可以编写自己的多态系统,该系统接受任意类型的对象并将它们的操作包装在一个值语义包装器中。


第四个选项是 CRTP 静态多态性。这不适合构建动态解析树,但可以用来帮助实现上述部分内容。


第五个选项是面向方面的 std::function 操作包。


第六个选项是手动函数表调整,基本上是手动重新实现 C++ vtable 解决方案,就像您在 C 中一样,但在 C++ 中。这可以让您拥有与其他面向对象语言类似的功能。


第七种选择是编写一个信号槽系统并向您的对象发送消息。


几乎肯定还有更多。

最简单的解决方法可能是先学习C++中的继承和虚函数(上面的第一个选项)。此时我个人可能会使用 std::variant 编写一个解析树,但如果您此时可能对 C++ 了解得不够多,无法实际执行此操作。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。