【问题标题】:How to build a recursive descent parser如何构建递归下降解析器
【发布时间】:2013-11-12 18:17:09
【问题描述】:

我一直在为一个简单的计算器开发递归下降解析器。当某个东西被声明时,它要么被声明为 int 要么被声明为 float。目前,我将字符串保存到两个不同的向量中,一个用于 int,一个用于 float。在这一点上,我不在乎关联的数字是什么,我只关心在使用之前声明字符串。

我的问题是,如果在诸如 float + int 之类的操作中使用 int 和 float,我必须能够输出警告消息。

所以如果表达式是术语+表达式或术语-表达式或术语。在递归下降中,我怎么可能检查一个 int 是否被用于带有浮点数的操作中。对不起,如果解释不清楚。我觉得解释起来有点困难。如果有必要,我已经添加了一些代码,我只是不想用代码淹没这个问题。

编辑: 仍然缺少一堆代码,我想抓住重要的部分,但如果需要,我可以上传整个东西。我看到有些人不明白主要问题是什么。要求之一是“当整数和浮点值混合在 +、-、* 和 / 中时,整数将转换为浮点数。打印一条消息,指示行号并且需要进行转换。”目前程序从文件中读取。如果你说“int x;”程序当前会将 x 保存在 int 向量中,然后当您说诸如 x=5 之类的内容时;它将承认 x 已被声明并且分配将通过。我的问题是如果你说 int x;浮动y;诠释z; x=5; y=7.5; z=x+y;我如何能够检查这一点,因为目前我的程序只保存变量的类型而不是值。本质上,我想知道是否有可能像扫描已完成的解析一样扫描字符串或其他使用 int 和 float 找出操作的方法。

lex 扫描器是用 flex 创建的

class Token {
Tokentype   type;
string      value;
int     linenum;

public:
Token(Tokentype t, string v="") {
    type = t;
    value = v;
}
Tokentype getType() { return type; }
string getValue() { return value; }
int getLinenum() { return linenum; }
};

vector<string> int_list;  
vector<string> float_list; 

class PTree {
PTreeNodetype   type;
PTree *left;
PTree *right;
public:
PTree(PTreeNodetype t, PTree *l=0, PTree *r=0) {
    type = t;
    left = l;
    right = r;
}
PTreeNodetype getType(){ return type;}
};

// expr ::= term PLUS expr | term MINUS expr | term 
PTree *
Expr() {

PTree *term = Term();
Token *t;

if (!term)
    return 0;

t = getToken();

if (t == NULL){
    delete t;
    return 0;
}
if(t->getType() != T_SC)
{
    if (t->getType() == T_RPAREN){
        pushbacktoken(t);
        return new PTree(EXPR, term);
    }

    if (t->getType() != T_PLUS && t->getType() != T_MINUS)
    {
        cout << t->getLinenum() <<  ":" << "Error:    expected + or -" << endl;
        pushbacktoken(t);
        delete t;
        return 0; 
    }



    delete t;
    PTree *expr = Expr();

    if (!expr)
        return 0;

    return new PTree(EXPR, term, expr);
}

pushbacktoken(t);
return new PTree(EXPR, term);
  }

【问题讨论】:

  • 这可能有助于向我们展示您正在努力解决的代码,这将是您问题的一个很好的补充。使用编辑链接按钮编辑您的帖子并使用代码块格式显示它:)
  • 你可以使用type inferencing,但这肯定会让困惑的人感到困惑。认真研究类型推断,但不要期望它很容易实现。您必须学习 unificationbackward-chaining 才能让它发挥作用。

标签: c++ parsing type-inference


【解决方案1】:

我认为您需要多解释一下代码的结构。

在您通常所说的口译员中,会发生三件事:

  1. 词法分析器/扫描器正在生成令牌流
  2. 解析器正在获取令牌并构建语义对象
  3. 解释器正在使用语义对象树并执行它们

第 1 阶段不需要关心您添加的是 int 和 float。第 2 阶段可以在您的语义对象/结构中填充一个警告字段,解释器在看到填充时将打印该警告字段,或者解释器可以自己识别此警告条件。

为了向您提供更多细节或使用更具体的术语,我们需要了解您如何表示操作。

【讨论】:

    【解决方案2】:

    我看到的两个选项,取决于你在做什么。

    首先。在构建解析树时不要担心。稍后,当你走树时,你可以很容易地检查这个并抛出错误。

    第二。intfloat 使用不同的规则。所以你会有一个添加两个整数的规则和一个添加两个浮点数的规则。这也意味着你不会有一个 number 规则,我猜你会这样做,它混合了整数和浮点数。

    我绝对推荐第一种方式。

    【讨论】:

      【解决方案3】:

      计算器传统上不“声明”事物,因此不清楚您的计算器在解析表达式时知道什么。

      如果我假设你在解析表达式“i*r”之前“声明 i int, r real”,你似乎有几个问题:

      a) 在解析 i 和 r 是否已声明时,您如何知道?技术上的答案是在解析过程中您不必知道;您可以解析、构建一棵树,并在以后进行此类检查。在实际层面上,人们经常将符号查找编织到解析过程中(随着您的语言变得更大,这会变得更加混乱,因此不建议将其用于计算器之外[您会发现大多数 C 编译器都这样做,增加了它们的混乱])。答案很简单:保留一个已定义符号字符串的列表,当您遇到标识符时,查看它是否在列表中。

      b) 你怎么知道“i”或“r”的类型?简单的。与符号字符串、声明的类型相关联,例如 , .相关的声明集通常称为符号表。

      c) 你怎么知道操作是否在相同(“正确”)类型的值上操作?在这里,您需要关联每个操作数,即它的“类型”。常量有明显的类型; 1.0 是实数,1 是整数。 “i”是整数,您的解析器知道它,因为它查找了类型(上图);类似地对于“r”。然后,每个表达式项都必须检查其操作数的兼容性。可能不明显的是每个表达式都必须计算它的结果类型,例如,3 * 4.0 是实数,而不是整数。所以在解析机器的同时,你需要传播一个类型。

      【讨论】:

        【解决方案4】:

        +1 虚空逻辑。他的回答应该让您对如何构建递归下降解析器有一个基本的了解。如果你的某个部分有问题,最好能更详细地了解你是如何构建代码的。

        如果您想查看其中的示例,请查看此implementation

        【讨论】:

        • 不错的 RDP!很干净!
        【解决方案5】:

        这本书可能会有所帮助:

        • 编译器:A. Aho、M. Lam 和 R. Sethi 的原则、技术和工具(“Dragon Book”)。

        这里有一组工具可以帮助你:

        • GNU 弹性
        • GNU 野牛

        【讨论】:

          猜你喜欢
          • 2015-08-07
          • 1970-01-01
          • 2012-05-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多