簡體   English   中英

誤解重復指令 - 它應該失敗,但沒有

[英]Misunderstanding repeat directive - it should fail, but doesn't

我想寫一個語法(高度簡化):

grr := integer [ . integer ]

integer ::= digit { [ underline ] digit }

由於稍后再次需要解析的文字(真正的語法更復雜,並非所有內容都可以立即轉換為數字),因此文字必須完全存儲為 AST 中的字符串(更准確地說是 iterator_range)以供以后使用(帶下划線) .

現在的問題是文字表達式可能比它們應該的更長(關於稍后的實現/計算等)。 顯而易見的解決方案是repeat指令(這里詳細說明 Qi 重復X3非常短)。

這是我的問題開始的地方( coliru ):

    for(std::string_view const s : {
        // ok
        "0", "10", "1_0", "012345", 
        // too long
        "0123456",
        "1_2_3_4_5_6_7_8_9_0", 
        // absolutely invalid
        "1_2_3_4_5_6_", "_0123_456", ""
    }) {
        auto const cs = x3::char_("0-9");
        std::string attr;
        bool const ok = x3::parse(std::begin(s), std::end(s), 
            x3::raw[ cs >> x3::repeat(0, 5)[ ('_' >> cs) | cs] ],
            attr);
        cout << s << " -> " << attr 
             << " (" << std::boolalpha << ok << ")"
             << "\n";   
    }

0 -> 0 (true)
10 -> 10 (true)
1_0 -> 1_0 (true)
012345 -> 012345 (true)
0123456 -> 012345 (true)
1_2_3_4_5_6_7_8_9_0 -> 1_2_3_4_5_6 (true)
1_2_3_4_5_6_ -> 1_2_3_4_5_6 (true)
_0123_456 ->  (false)
 ->  (false)

如果文字太長,解析器應該會失敗,但它不會。 如果它以下划線結尾,它也應該這樣做——但事實並非如此。 開頭的下划線和空文字被正確識別/解析為假。

同時,我嘗試將更復雜的解析器寫入單獨的解析器類,但在這里我錯過了識別以下划線結尾的文字的規則......

此外, BOOST_SPIRIT_X3_DEBUG 似乎突然壞了 - 沒有輸出。

我的問題的解決方案是什么? 除了通過迭代器,計數器等絕對低級和復雜之外,我沒有想法。

這個問題也會影響其他要實施的規則。

如果文字太長,解析器應該會失敗

它在哪里說呢? 看起來代碼完全符合您的要求:它最多解析 6 位數字,並帶有必要的下划線。 輸出甚至證實它確實做到了。

您當然可以通過顯示解析的內容來使其更加明顯:

住在科利魯

auto f = begin(s), l = end(s);
bool const ok = x3::parse(
    f, l, x3::raw[cs >> x3::repeat(0, 5)[('_' >> cs) | cs]], attr);

fmt::print(
    "{:21} -> {:5} {:13} remaining '{}'\n",
    fmt::format("'{}'", s),
    ok,
    fmt::format("'{}'", attr),
    std::string(f, l));

印刷

'0'                   -> true  '0'           remaining ''
'10'                  -> true  '10'          remaining ''
'1_0'                 -> true  '1_0'         remaining ''
'012345'              -> true  '012345'      remaining ''
'0123456'             -> true  '012345'      remaining '6'
'1_2_3_4_5_6_7_8_9_0' -> true  '1_2_3_4_5_6' remaining '_7_8_9_0'
'1_2_3_4_5_6_'        -> true  '1_2_3_4_5_6' remaining '_'
'_0123_456'           -> false ''            remaining '_0123_456'
''                    -> false ''            remaining ''

修復

要斷言解析完整的輸入,請使用x3::eoi或檢查迭代器:

住在科利魯

bool const ok = x3::parse(
    f,
    l,
    x3::raw[cs >> x3::repeat(0, 5)[('_' >> cs) | cs]] >> x3::eoi,
    attr);

印刷

'0'                   -> true  '0'           remaining ''
'10'                  -> true  '10'          remaining ''
'1_0'                 -> true  '1_0'         remaining ''
'012345'              -> true  '012345'      remaining ''
'0123456'             -> false '012345'      remaining '0123456'
'1_2_3_4_5_6_7_8_9_0' -> false '1_2_3_4_5_6' remaining '1_2_3_4_5_6_7_8_9_0'
'1_2_3_4_5_6_'        -> false '1_2_3_4_5_6' remaining '1_2_3_4_5_6_'
'_0123_456'           -> false ''            remaining '_0123_456'
''                    -> false ''            remaining ''

不同的詞素

相反,如果您想允許輸入繼續,而不是某些字符,例如解析許多這樣的“數字”:

auto const number = x3::lexeme[ //
    x3::raw[cs >> x3::repeat(0, 5)[('_' >> cs) | cs]]
    // within the lexeme, assert that no digit or _ follows
    >> ! (cs | '_') //
];

住在科利魯

//#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
#include <fmt/ranges.h>
using namespace std::string_view_literals;

namespace Parser {
    namespace x3 = boost::spirit::x3;
    auto const cs = x3::digit;
    auto const number = x3::lexeme[ //
        x3::raw[cs >> x3::repeat(0, 5)[('_' >> cs) | cs]]
        // within the lexeme, assert that no digit or _ follows
        >> ! (cs | '_') //
    ];
    auto const ws_or_comment = x3::space | "//" >> *~x3::char_("\r\n");
    auto const numbers = x3::skip(ws_or_comment)[number % ','];
} // namespace Parser

int main()
{
    std::vector<std::string> attr;
    std::string_view const s =
        R"(0,
           10,
           1_0,
           012345,
           // too long
           0123456,
           1_2_3_4_5_6_7_8_9_0,
           // absolutely invalid
           1_2_3_4_5_6_,
           _0123_456)"sv;

    auto f = begin(s), l = end(s);
    bool const ok = parse(f, l, Parser::numbers, attr);

    fmt::print("{}: {}\nremaining '{}'\n", ok, attr, std::string(f, l));
}

印刷

true: ["0", "10", "1_0", "012345"]
remaining ',
           // too long
           0123456,
           1_2_3_4_5_6_7_8_9_0,
           // absolutely invalid
           1_2_3_4_5_6_,
           _0123_456'

證明它

在存在其他無關緊要的空白的情況下,要深入檢查詞位內部的要點:

auto const numbers = x3::skip(ws_or_comment)[*number];

稍微調整一下測試輸入(去掉逗號):

住在科利魯

//#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
#include <fmt/ranges.h>
using namespace std::string_view_literals;

namespace Parser {
    namespace x3 = boost::spirit::x3;
    auto const cs = x3::digit;
    auto const number = x3::lexeme[ //
        x3::raw[cs >> x3::repeat(0, 5)[('_' >> cs) | cs]]
        // within the lexeme, assert that no digit or _ follows
        >> ! (cs | '_') //
    ];
    auto const ws_or_comment = x3::space | "//" >> *~x3::char_("\r\n");
    auto const numbers = x3::skip(ws_or_comment)[*number];
} // namespace Parser

int main()
{
    std::vector<std::string> attr;
    std::string_view const s =
        R"(0
           10
           1_0
           012345
           // too long
           0123456
           1_2_3_4_5_6_7_8_9_0
           // absolutely invalid
           1_2_3_4_5_6_
           _0123_456)"sv;

    auto f = begin(s), l = end(s);
    bool const ok = parse(f, l, Parser::numbers, attr);

    fmt::print("{}: {}\nremaining '{}'\n", ok, attr, std::string(f, l));
}

印刷

true: ["0", "10", "1_0", "012345"]
remaining '0123456
           1_2_3_4_5_6_7_8_9_0
           // absolutely invalid
           1_2_3_4_5_6_
           _0123_456'

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM