簡體   English   中英

使用帶有一些自定義 bnf 規則的 Boost Spirit 解析 BNF 語法

[英]Parsing BNF Grammar using Boost Spirit with some custom bnf rules

假設我有如下所示的 BNF 語法。 現在,“列表”將對應於“|”之前的所有術語象征。 但是,我想讀取每個“列表”的最后一個數字作為“列表”的屬性。

<code> ::= <code> <line> 12 2 | <line> 24 4 
<line> ::= <ifte> 13 23 | <loop> 24 34 | <action> 15 3 
<ifte> ::= if <cond> {<code>} else {<code>} 12

此外,最后一個數字(列表屬性)可以是可選的; 我想為了讓這更容易,我可能不得不使用一些符號將數字括起來以便於解析,例如 <<23>>。

這里代碼可以編譯,但它不解析上面的語法:

//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>
/*#include <fmt/ranges.h>
#include <fmt/ostream.h>*/
#include <iomanip>

namespace AST {

    struct Name : std::string {
        using std::string::string;
        using std::string::operator=;

        friend std::ostream &operator<<(std::ostream &os, Name const &n) {
            return os << '<' << n.c_str() << '>';
        }
    };

    using Term = boost::variant<Name, std::string>;

    struct List {
        std::vector<Term> terms;
        int number;
    };

    using Expression = std::vector<List>;

    struct Rule {
        Name name; //rhs
        Expression rhs;
    };

    using Syntax = std::vector<Rule>;
}
BOOST_FUSION_ADAPT_STRUCT(AST::List, terms, number)
BOOST_FUSION_ADAPT_STRUCT(AST::Rule, name, rhs)

namespace Parser {

    namespace qi = boost::spirit::qi;
    template<typename Iterator>
    class BNF : public qi::grammar<Iterator, AST::Syntax()> {
    public:
        BNF() : BNF::base_type(start) {
            start       = qi::skip(blank)[rule % +qi::eol];
            _rule_name  = qi::hold[qi::char_('<') >> (qi::alpha >> *(qi::alnum | qi::char_('-'))) >> qi::char_('>')];
            _list       = +term >> qi::uint_;
            term        = _literal | _rule_name;
            _literal    = qi::hold['"' >> *(character - '"') >> '"']
                        | qi::hold["'" >> *(character - "'") >> "'"]
                        | qi::hold[+(qi::graph - qi::char_("<|>") - "::=")];
            character   = qi::alnum | qi::char_("\"'| !#$%&()*+,./:;>=<?@]\\^_`{}~[-");
            _expression = _list % '|';

            rule = _rule_name >> "::=" >> _expression;

            BOOST_SPIRIT_DEBUG_NODES((rule)(_expression)(_list)(term)(_literal)(
                character)(_rule_name))
        }

    private:
        qi::rule<Iterator> blank;
        qi::rule<Iterator, AST::Syntax()>     start;
        qi::rule<Iterator, AST::Rule(),       qi::rule<Iterator>> rule;
        qi::rule<Iterator, AST::Expression(), qi::rule<Iterator>> _expression;
        qi::rule<Iterator, AST::List(),       qi::rule<Iterator>> _list;
        qi::rule<Iterator, AST::Term()>       term;
        qi::rule<Iterator, AST::Name()>       _rule_name;
        qi::rule<Iterator, std::string()>     _literal;
        qi::rule<Iterator, char()>            character;
    };
}

int main() {
    Parser::BNF<std::string::const_iterator> const  parser;
}

如何修復/修改上面的代碼鏈接以滿足我的需要。

我認為尚不清楚您要支持哪種輸入語法。

例如,

  1. 當列表屬性可以是可選的,這是否意味着代替<code> <line> 12 2這也將是一個沒有屬性的有效列表: <code> <line> 12 2 您將如何避免將12解析為屬性?
  2. 您的輸入使用 {} 中的名稱 - 您清楚地顯示的解析器實現不支持這些名稱。 你需要支持嗎? 如何?

讓我們同時解決它們

廣告 2.: 修正你的輸入

假設您真的不希望 {} 具有神奇的含義,而是打算將它們作為語法中的文字。 像“if”和“else”一樣,它們需要是文字,所以:

<ifte> ::= 'if' <cond> '{' <code> '}' 'else' '{' <code> '}' 23

或者

<ifte> ::= "if" <cond> "{" <code> "}" "else" "{" <code> "}" 23

這修復了您的示例: Live On Compiler Explorer

code ::= <code><line> 34 | <line> 34
line ::= <ifte> 23 | <loop> 34 | <action> 23
ifte ::= if<cond>{<code>}else{<code>} 23
Remaining: "
"

廣告 1.:可選屬性

讓我們表達我們的意圖:

using ListAttribute = int;

struct List {
    std::list<Term> terms;
    ListAttribute attribute;
};

然后在語法中添加一個詞素規則(沒有船長):

qi::rule<Iterator, Ast::ListAttribute()> _attribute;

然后我們實現如下:

_attribute  = lexeme [ "<<" >> qi::uint_ >> ">>" ] 
            | qi::attr(0);
_list       = +_term >> _attribute;

現在它將只識別 <> 作為列表屬性:

Live On 編譯器資源管理器

std::string const input =
    "<code> ::= <code> <line> | <line>\n"
    "<line> ::= <ifte> | <loop> | <action>\n"
    "<ifte> ::= 'if' <cond> '{' <code> '}' 'else' '{' <code> '}'\n"

    "<code> ::= <code> <line> <<34>> | <line> <<34>>\n"
    "<line> ::= <ifte> <<23>> | <loop> <<34>> | <action> <<23>>\n"
    "<ifte> ::= 'if' <cond> '{' <code> '}' 'else' '{' <code> '}' <<23>>\n"

    // and the disambiguated example from the question
    "<code> ::= <code> <line> '34' | <line> '12' <<2>>\n"
;

印刷

code ::= <code><line> 0 | <line> 0
line ::= <ifte> 0 | <loop> 0 | <action> 0
ifte ::= if<cond>{<code>}else{<code>} 0
code ::= <code><line> 34 | <line> 34
line ::= <ifte> 23 | <loop> 34 | <action> 23
ifte ::= if<cond>{<code>}else{<code>} 23
code ::= <code><line>34 0 | <line>12 2
Remaining: "
"

摘要/獎金

我剛剛意識到您不需要在12 212 (缺少屬性)之間消除歧義,因為12無論如何都不是有效的輸入標記(文字/名稱以<"'之一開頭),所以這里是:

Live On 編譯器資源管理器

//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>
#include <fmt/ranges.h>
#include <fmt/ostream.h>
#include <iomanip>
namespace qi = boost::spirit::qi;

namespace Ast {
    struct Name : std::string {
        using std::string::string;
        using std::string::operator=;

        friend std::ostream& operator<<(std::ostream& os, Name const& n) {
            return os << '<' << n.c_str() << '>';
        }
    };

    using Term = boost::variant<Name, std::string>;

    using ListAttribute = int;

    struct List {
        std::list<Term> terms;
        ListAttribute attribute;

        friend std::ostream& operator<<(std::ostream& os, List const& l) {
            for (auto& t : l.terms)
                os << t;
            return os << " " << l.attribute;
        }
    };

    using Expression = std::list<List>;

    struct Rule {
        Name name; // lhs
        Expression rhs;
    };

    using Syntax = std::list<Rule>;
}

BOOST_FUSION_ADAPT_STRUCT(Ast::List, terms, attribute)
BOOST_FUSION_ADAPT_STRUCT(Ast::Rule, name, rhs)

namespace Parser {
    template <typename Iterator>
    struct BNF: qi::grammar<Iterator, Ast::Syntax()> {
        BNF(): BNF::base_type(start) {
            using namespace qi;
            start = skip(blank) [ _rule % +eol ];

            _rule       = _rule_name >> "::=" >> _expression;
            _expression = _list % '|';
            _attribute  = uint_ | qi::attr(0);
            _list       = +_term >> _attribute;
            _term       = _literal | _rule_name ;
            _literal    = '"' >> *(_character - '"') >> '"'
                        | "'" >> *(_character - "'") >> "'";
            _character  = alnum | char_("\"'| !#$%&()*+,./:;>=<?@]\\^_`{}~[-");
            _rule_name  = '<' >> (alpha >> *(alnum | char_('-'))) >> '>';

            BOOST_SPIRIT_DEBUG_NODES(
                (_rule)(_expression)(_list)(_attribute)(_term)
                (_literal)(_character)
                (_rule_name))
        }

      private:
        qi::rule<Iterator, Ast::Syntax()>     start;
        qi::rule<Iterator, Ast::Rule(),       qi::blank_type> _rule;
        qi::rule<Iterator, Ast::Expression(), qi::blank_type> _expression;
        qi::rule<Iterator, Ast::List(),       qi::blank_type> _list;
        // lexemes
        qi::rule<Iterator, Ast::ListAttribute()> _attribute;
        qi::rule<Iterator, Ast::Term()>          _term;
        qi::rule<Iterator, Ast::Name()>          _rule_name;
        qi::rule<Iterator, std::string()>        _literal;
        qi::rule<Iterator, char()>               _character;
    };
}

int main() {
    Parser::BNF<std::string::const_iterator> const parser;

    std::string const input =
        "<code> ::= <code> <line> | <line>\n"
        "<line> ::= <ifte> | <loop> | <action>\n"
        "<ifte> ::= 'if' <cond> '{' <code> '}' 'else' '{' <code> '}'\n"

        "<code> ::= <code> <line> 34 | <line> 34\n"
        "<line> ::= <ifte> 23 | <loop> 34 | <action> 23\n"
        "<ifte> ::= 'if' <cond> '{' <code> '}' 'else' '{' <code> '}' 23\n"

        // and the disambiguated example from the question
        "<code> ::= <code> <line> '34' | <line> '12' 2\n"
    ;

    auto it = input.begin(), itEnd = input.end();

    Ast::Syntax syntax;
    if (parse(it, itEnd, parser, syntax)) {
        for (auto& rule : syntax)
            fmt::print("{} ::= {}\n", rule.name, fmt::join(rule.rhs, " | "));
    } else {
        std::cout << "Failed\n";
    }

    if (it != itEnd)
        std::cout << "Remaining: " << std::quoted(std::string(it, itEnd)) << "\n";
}

印刷

code ::= <code><line> 0 | <line> 0
line ::= <ifte> 0 | <loop> 0 | <action> 0
ifte ::= if<cond>{<code>}else{<code>} 0
code ::= <code><line> 34 | <line> 34
line ::= <ifte> 23 | <loop> 34 | <action> 23
ifte ::= if<cond>{<code>}else{<code>} 23
code ::= <code><line>34 0 | <line>12 2
Remaining: "
"

暫無
暫無

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

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