c++ - "carbon-copy"c++ istream?

标签 c++ parsing istream

对于我自己的小解析器框架,我试图定义(类似于)以下函数:

template <class T>
// with operator>>( std::istream&, T& )
void tryParse( std::istream& is, T& tgt )
{
    is >> tgt /* , *BUT* store every character that is consumed by this operation
    in some string. If afterwards, is.fail() (which should indicate a parsing
    error for now), put all the characters read back into the 'is' stream so that
    we can try a different parser. */
}

然后我可以这样写:(也许不是最好的例子)

/* grammar: MyData     = <IntTriple> | <DoublePair>
            DoublePair = <double> <double>
            IntTriple  = <int> <int> <int> */
class MyData
{ public:
    union { DoublePair dp; IntTriple it; } data;
    bool isDoublePair;
};

istream& operator>>( istream& is, MyData& md )
{
    /* If I used just "is >> md.data.it" here instead, the
       operator>>( ..., IntTriple ) might consume two ints, then hit an
       unexpected character, and fail, making it impossible to read these two
       numbers as doubles in the "else" branch below. */
    tryParse( is, md.data.it );
    if ( !is.fail() )
        md.isDoublePair = false;
    else
    {
        md.isDoublePair = true;
        is.clear();
        is >> md.data.dp;
    }
    return is;
}

非常感谢任何帮助。

最佳答案

不幸的是,流只有非常少和基本的回放支持。

上次我需要这个时,我编写了自己的读取器类,它包装了一个流,但有一个缓冲区可以将内容放回其中,并且仅当该缓冲区为空时才从流中读取。这些有从中获取状态的方法,您可以提交状态或回滚到较早的状态。
状态类的析构函数中的默认操作是回滚,这样您就可以提前解析而无需过多考虑错误处理,因为异常只会将解析器的状态回滚到尝试不同语法规则的点。 (我认为这称为回溯。)这是一个草图:

class parse_buffer {
    friend class parse_state;
public:
    typedef std::string::size_type index_type;

    parse_buffer(std::istream& str);

    index_type get_current_index() const;
    void set_current_index(index_type) const;

    std::string get_next_string(bool skip_ws = true) const;
    char get_next_char(bool skip_ws = true);
    char peek_next_char(bool skip_ws = true); 

    std::string get_error_string() const; // returns string starting at error idx
    index_type get_error_index() const;
    void set_error_index(index_type);

    bool eof() const;

    // ...
};

class parse_state {
public:
    parse_state(parse_buffer&);
    ~parse_state();

    void commit();
    void rollback();

    // ...
};

这应该会给你一个想法。它没有任何实现,但这很简单,应该很容易重做。此外,真正的代码有许多方便的功能,例如读取定界字符串的读取函数,如果它是几个给定关键字之一则使用字符串,读取字符串并将其转换为每个模板参数给定的类型,等等。

这个想法是,一个函数会将错误索引设置到它的起始位置,保存解析状态,并尝试解析直到它成功或进入死胡同。在后一种情况下,它只会抛出一个异常。这将破坏堆栈上的 parse_state 对象,将状态回滚到一个可以捕获异常并尝试其他操作或输出错误的函数(这是 get_error_string() 进来了。)

如果你想要一个非常快的解析器,这个策略可能是错误的,但是流通常也很慢。 OTOH,上次我使用类似的东西时,我制作了一个在专有 DOM 上运行的 XPath 解析器,该 DOM 用于表示 3D 渲染器中的场景。那些试图获得更高帧率的人热衷于此的并不是 XPath 解析器。 :)

关于c++ - "carbon-copy"c++ istream?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3778268/

相关文章:

c++ - 为什么 cmake 选错了库?

c++ - 使用 ofstream 附加到文件

php - 是否有可用的 PHP DocBlock 解析器工具?

c# - 在 C# 中使用正则表达式解析电子邮件

java - 使用 xpath 更新 xml

c++ - 从 ifstream 读取 stdin/cin 是否安全?

c++ - 为什么 std::getline() 在格式化提取后跳过输入?

c++ - 具有类名的数据成员

c++ - 为什么 visual studio code 告诉我 cout 不是 std 命名空间的成员?

c++ - 将 std::unique_ptr 与 std::istream 一起使用?