gpt4 book ai didi

c++ - 无法获得boost::spirit解析器和词法分析器,用于除std::string或int或double以外的 token 类型

转载 作者:太空狗 更新时间:2023-10-29 22:56:41 24 4
gpt4 key购买 nike

这不会编译(下面的代码)。

这里还有另一个问题,同样的错误。但是我不明白答案。我已经尝试过将qi::eps插入位置-但没有成功。

我还尝试过为使用的类型添加元函数(boost::spirit::raits::is_container),但这也无济于事。

我还尝试使用相同的变体,其中包含我需要在所有地方使用的所有类型。同样的问题。

是否有人为词法分析程序返回double或int或string以外的东西工作?对于解析器,还返回非平凡的对象吗?

我尝试在返回默认对象的任何地方实现语义功能。但这也无济于事。

代码如下:

// spirit_error.cpp : Defines the entry point for the console application.
//

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/phoenix/object.hpp>
#include <boost/spirit/include/qi_char_class.hpp>
#include <boost/spirit/include/phoenix_bind.hpp>
#include <boost/mpl/index_of.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/smart_ptr/intrusive_ref_counter.hpp>

namespace lex = boost::spirit::lex;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;


namespace frank
{
class ref_counter:public boost::intrusive_ref_counter<ref_counter>
{ public:
virtual ~ref_counter(void)
{
}
};
class symbol:public ref_counter
{ public:
typedef boost::intrusive_ptr<const symbol> symbolPtr;
typedef std::vector<symbolPtr> symbolVector;
struct push_scope
{ push_scope()
{
}
~push_scope(void)
{
}
};
};
class nature:public symbol
{ public:
enum enumAttribute
{ eAbstol,
eAccess,
eDDT,
eIDT,
eUnits
};
struct empty
{ bool operator<(const empty&) const
{ return false;
}
friend std::ostream &operator<<(std::ostream &_r, const empty&)
{ return _r;
}
};
typedef boost::variant<empty, std::string> attributeValue;
};
class discipline:public symbol
{ public:
enum enumDomain
{ eDiscrete,
eContinuous
};
};

class type:public ref_counter
{ public:
typedef boost::intrusive_ptr<type> typePtr;
};
struct myIterator:std::iterator<std::random_access_iterator_tag, char, std::ptrdiff_t, const char*, const char&>
{ std::string *m_p;
std::size_t m_iPos;
myIterator(void)
:m_p(nullptr),
m_iPos(~std::size_t(0))
{
}
myIterator(std::string &_r, const bool _bEnd = false)
:m_p(&_r),
m_iPos(_bEnd ? ~std::size_t(0) : 0)
{
}
myIterator(const myIterator &_r)
:m_p(_r.m_p),
m_iPos(_r.m_iPos)
{
}
myIterator &operator=(const myIterator &_r)
{ if (this != &_r)
{ m_p = _r.m_p;
m_iPos = _r.m_iPos;
}
return *this;
}
const char &operator*(void) const
{ return m_p->at(m_iPos);
}
bool operator==(const myIterator &_r) const
{ return m_p == _r.m_p && m_iPos == _r.m_iPos;
}
bool operator!=(const myIterator &_r) const
{ return m_p != _r.m_p || m_iPos != _r.m_iPos;
}
myIterator &operator++(void)
{ ++m_iPos;
if (m_iPos == m_p->size())
m_iPos = ~std::size_t(0);
return *this;
}
myIterator operator++(int)
{ const myIterator s(*this);
operator++();
return s;
}
myIterator &operator--(void)
{ --m_iPos;
return *this;
}
myIterator operator--(int)
{ const myIterator s(*this);
operator--();
return s;
}
bool operator<(const myIterator &_r) const
{ if (m_p == _r.m_p)
return m_iPos < _r.m_iPos;
else
return m_p < _r.m_p;
}
std::ptrdiff_t operator-(const myIterator &_r) const
{ return m_iPos - _r.m_iPos;
}
};
struct onInclude
{ auto operator()(myIterator &_rStart, myIterator &_rEnd) const
{ // erase what has been matched (the include statement)
_rStart.m_p->erase(_rStart.m_iPos, _rEnd.m_iPos - _rStart.m_iPos);
// and insert the contents of the file
_rStart.m_p->insert(_rStart.m_iPos, "abcd");
_rEnd = _rStart;
return lex::pass_flags::pass_ignore;
}
};
template<typename LEXER>
class lexer:public lex::lexer<LEXER>
{ public:
lex::token_def<type::typePtr> m_sKW_real, m_sKW_integer, m_sKW_string;
lex::token_def<lex::omit> m_sLineComment, m_sCComment;
lex::token_def<lex::omit> m_sWS;
lex::token_def<lex::omit> m_sSemicolon, m_sEqual, m_sColon, m_sInclude, m_sCharOP, m_sCharCP,
m_sComma;
lex::token_def<std::string> m_sIdentifier, m_sString;
lex::token_def<double> m_sReal;
lex::token_def<int> m_sInteger;
lex::token_def<lex::omit> m_sKW_units, m_sKW_access, m_sKW_idt_nature, m_sKW_ddt_nature, m_sKW_abstol,
m_sKW_nature, m_sKW_endnature, m_sKW_continuous, m_sKW_discrete,
m_sKW_potential, m_sKW_flow, m_sKW_domain, m_sKW_discipline, m_sKW_enddiscipline, m_sKW_module,
m_sKW_endmodule, m_sKW_parameter;
//typedef const type *typePtr;
template<typename T>
struct extractValue
{ T operator()(const myIterator &_rStart, const myIterator &_rEnd) const
{ return boost::lexical_cast<T>(std::string(_rStart, _rEnd));
}
};
struct extractString
{ std::string operator()(const myIterator &_rStart, const myIterator &_rEnd) const
{ const auto s = std::string(_rStart, _rEnd);
return s.substr(1, s.size() - 2);
}
};
lexer(void)
:m_sWS("[ \\t\\n\\r]+"),
m_sKW_parameter("\"parameter\""),
m_sKW_real("\"real\""),
m_sKW_integer("\"integer\""),
m_sKW_string("\"string\""),
m_sLineComment("\\/\\/[^\\n]*"),
m_sCComment("\\/\\*"
"("
"[^*]"
"|" "[\\n]"
"|" "([*][^/])"
")*"
"\\*\\/"),
m_sSemicolon("\";\""),
m_sEqual("\"=\""),
m_sColon("\":\""),
m_sCharOP("\"(\""),
m_sCharCP("\")\""),
m_sComma("\",\""),
m_sIdentifier("[a-zA-Z_]+[a-zA-Z0-9_]*"),
m_sString("[\\\"]"
//"("
// "(\\[\"])"
// "|"
//"[^\"]"
//")*"
"[^\\\"]*"
"[\\\"]"),
m_sKW_units("\"units\""),
m_sKW_access("\"access\""),
m_sKW_idt_nature("\"idt_nature\""),
m_sKW_ddt_nature("\"ddt_nature\""),
m_sKW_abstol("\"abstol\""),
m_sKW_nature("\"nature\""),
m_sKW_endnature("\"endnature\""),
m_sKW_continuous("\"continuous\""),
m_sKW_discrete("\"discrete\""),
m_sKW_domain("\"domain\""),
m_sKW_discipline("\"discipline\""),
m_sKW_enddiscipline("\"enddiscipline\""),
m_sKW_potential("\"potential\""),
m_sKW_flow("\"flow\""),
//realnumber ({uint}{exponent})|((({uint}\.{uint})|(\.{uint})){exponent}?)
//exponent [Ee][+-]?{uint}
//uint [0-9][_0-9]*

m_sReal("({uint}{exponent})"
"|"
"("
"(({uint}[\\.]{uint})|([\\.]{uint})){exponent}?"
")"
),
m_sInteger("{uint}"),
m_sInclude("\"`include\""),
m_sKW_module("\"module\""),
m_sKW_endmodule("\"endmodule\"")
{ this->self.add_pattern
("uint", "[0-9]+")
("exponent", "[eE][\\+\\-]?{uint}");
this->self = m_sSemicolon
| m_sEqual
| m_sColon
| m_sCharOP
| m_sCharCP
| m_sComma
| m_sString[lex::_val = boost::phoenix::bind(extractString(), lex::_start, lex::_end)]
| m_sKW_real//[lex::_val = boost::phoenix::bind(&type::getReal)]
| m_sKW_integer//[lex::_val = boost::phoenix::bind(&type::getInteger)]
| m_sKW_string//[lex::_val = boost::phoenix::bind(&type::getString)]
| m_sKW_parameter
| m_sKW_units
| m_sKW_access
| m_sKW_idt_nature
| m_sKW_ddt_nature
| m_sKW_abstol
| m_sKW_nature
| m_sKW_endnature
| m_sKW_continuous
| m_sKW_discrete
| m_sKW_domain
| m_sKW_discipline
| m_sKW_enddiscipline
| m_sReal[lex::_val = boost::phoenix::bind(extractValue<double>(), lex::_start, lex::_end)]
| m_sInteger[lex::_val = boost::phoenix::bind(extractValue<int>(), lex::_start, lex::_end)]
| m_sKW_potential
| m_sKW_flow
| m_sKW_module
| m_sKW_endmodule
| m_sIdentifier
| m_sInclude [ lex::_state = "INCLUDE" ]
;
this->self("INCLUDE") += m_sString [
lex::_state = "INITIAL", lex::_pass = boost::phoenix::bind(onInclude(), lex::_start, lex::_end)
];
this->self("WS") = m_sWS
| m_sLineComment
| m_sCComment
;
}
};
template<typename Iterator, typename Lexer>
class natureParser:public qi::grammar<Iterator, symbol::symbolPtr(void), qi::in_state_skipper<Lexer> >
{ qi::rule<Iterator, symbol::symbolPtr(void), qi::in_state_skipper<Lexer> > m_sStart;
qi::rule<Iterator, std::pair<nature::enumAttribute, nature::attributeValue>(void), qi::in_state_skipper<Lexer> > m_sProperty;
qi::rule<Iterator, std::string(), qi::in_state_skipper<Lexer> > m_sName;
public:
template<typename Tokens>
natureParser(const Tokens &_rTokens)
:natureParser::base_type(m_sStart)
{ m_sProperty = (_rTokens.m_sKW_units
>> _rTokens.m_sEqual
>> _rTokens.m_sString
>> _rTokens.m_sSemicolon
)
| (_rTokens.m_sKW_access
>> _rTokens.m_sEqual
>> _rTokens.m_sIdentifier
>> _rTokens.m_sSemicolon
)
| (_rTokens.m_sKW_idt_nature
>> _rTokens.m_sEqual
>> _rTokens.m_sIdentifier
>> _rTokens.m_sSemicolon
)
| (_rTokens.m_sKW_ddt_nature
>> _rTokens.m_sEqual
>> _rTokens.m_sIdentifier
>> _rTokens.m_sSemicolon
)
| (_rTokens.m_sKW_abstol
>> _rTokens.m_sEqual
>> _rTokens.m_sReal
>> _rTokens.m_sSemicolon
)
;
m_sName = (_rTokens.m_sColon >> _rTokens.m_sIdentifier);
m_sStart = (_rTokens.m_sKW_nature
>> _rTokens.m_sIdentifier
>> -m_sName
>> _rTokens.m_sSemicolon
>> *(m_sProperty)
>> _rTokens.m_sKW_endnature
);
m_sStart.name("start");
m_sProperty.name("property");
}
};
/*
// Conservative discipline
discipline electrical;
potential Voltage;
flow Current;
enddiscipline
*/
// a parser for a discipline declaration
template<typename Iterator, typename Lexer>
class disciplineParser:public qi::grammar<Iterator, symbol::symbolPtr(void), qi::in_state_skipper<Lexer> >
{ qi::rule<Iterator, symbol::symbolPtr(void), qi::in_state_skipper<Lexer> > m_sStart;
typedef std::pair<bool, boost::intrusive_ptr<const nature> > CPotentialAndNature;
struct empty
{ bool operator<(const empty&) const
{ return false;
}
friend std::ostream &operator<<(std::ostream &_r, const empty&)
{ return _r;
}
};
typedef boost::variant<empty, CPotentialAndNature, discipline::enumDomain> property;
qi::rule<Iterator, discipline::enumDomain(), qi::in_state_skipper<Lexer> > m_sDomain;
qi::rule<Iterator, property(void), qi::in_state_skipper<Lexer> > m_sProperty;
public:
template<typename Tokens>
disciplineParser(const Tokens &_rTokens)
:disciplineParser::base_type(m_sStart)
{ m_sDomain = _rTokens.m_sKW_continuous
| _rTokens.m_sKW_discrete
;
m_sProperty = (_rTokens.m_sKW_potential >> _rTokens.m_sIdentifier >> _rTokens.m_sSemicolon)
| (_rTokens.m_sKW_flow >> _rTokens.m_sIdentifier >> _rTokens.m_sSemicolon)
| (_rTokens.m_sKW_domain >> m_sDomain >> _rTokens.m_sSemicolon)
;
m_sStart = (_rTokens.m_sKW_discipline
>> _rTokens.m_sIdentifier
>> _rTokens.m_sSemicolon
>> *m_sProperty
>> _rTokens.m_sKW_enddiscipline
);
}
};
template<typename Iterator, typename Lexer>
class moduleParser:public qi::grammar<Iterator, symbol::symbolPtr(void), qi::in_state_skipper<Lexer> >
{ public:
qi::rule<Iterator, symbol::symbolPtr(void), qi::in_state_skipper<Lexer> > m_sStart;
qi::rule<Iterator, symbol::symbolVector(void), qi::in_state_skipper<Lexer> > m_sModulePortList;
qi::rule<Iterator, symbol::symbolVector(void), qi::in_state_skipper<Lexer> > m_sPortList;
qi::rule<Iterator, symbol::symbolPtr(void), qi::in_state_skipper<Lexer> > m_sPort;
qi::rule<Iterator, std::shared_ptr<symbol::push_scope>(void), qi::in_state_skipper<Lexer> > m_sModule;

typedef boost::intrusive_ptr<const ref_counter> intrusivePtr;
typedef std::vector<intrusivePtr> vectorOfPtr;
qi::rule<Iterator, vectorOfPtr(void), qi::in_state_skipper<Lexer> > m_sModuleItemList;
qi::rule<Iterator, intrusivePtr(void), qi::in_state_skipper<Lexer> > m_sParameter;
qi::rule<Iterator, intrusivePtr(void), qi::in_state_skipper<Lexer> > m_sModuleItem;
qi::rule<Iterator, type::typePtr(void), qi::in_state_skipper<Lexer> > m_sType;

template<typename Tokens>
moduleParser(const Tokens &_rTokens)
:moduleParser::base_type(m_sStart)
{ m_sPort = _rTokens.m_sIdentifier;
m_sPortList %= m_sPort % _rTokens.m_sComma;
m_sModulePortList %= _rTokens.m_sCharOP >> m_sPortList >> _rTokens.m_sCharCP;
m_sModule = _rTokens.m_sKW_module;
m_sType = _rTokens.m_sKW_real | _rTokens.m_sKW_integer | _rTokens.m_sKW_string;
m_sParameter = _rTokens.m_sKW_parameter
>> m_sType
>> _rTokens.m_sIdentifier
;
m_sModuleItem = m_sParameter;
m_sModuleItemList %= *m_sModuleItem;
m_sStart = (m_sModule
>> _rTokens.m_sIdentifier
>> m_sModulePortList
>> m_sModuleItemList
>> _rTokens.m_sKW_endmodule);
}
};
template<typename Iterator, typename Lexer>
class fileParser:public qi::grammar<Iterator, symbol::symbolVector(void), qi::in_state_skipper<Lexer> >
{ public:
disciplineParser<Iterator, Lexer> m_sDiscipline;
natureParser<Iterator, Lexer> m_sNature;
moduleParser<Iterator, Lexer> m_sModule;
qi::rule<Iterator, symbol::symbolVector(void), qi::in_state_skipper<Lexer> > m_sStart;
qi::rule<Iterator, symbol::symbolPtr(void), qi::in_state_skipper<Lexer> > m_sItem;
//public:
template<typename Tokens>
fileParser(const Tokens &_rTokens)
:fileParser::base_type(m_sStart),
m_sNature(_rTokens),
m_sDiscipline(_rTokens),
m_sModule(_rTokens)
{ m_sItem = m_sDiscipline | m_sNature | m_sModule;
m_sStart = *m_sItem;
}
};
}
int main()
{ std::string sInput = "\
nature Current;\n\
units = \"A\";\n\
access = I;\n\
idt_nature = Charge;\n\
abstol = 1e-12;\n\
endnature\n\
\n\
// Charge in coulombs\n\
nature Charge;\n\
units = \"coul\";\n\
access = Q;\n\
ddt_nature = Current;\n\
abstol = 1e-14;\n\
endnature\n\
\n\
// Potential in volts\n\
nature Voltage;\n\
units = \"V\";\n\
access = V;\n\
idt_nature = Flux;\n\
abstol = 1e-6;\n\
endnature\n\
\n\
discipline electrical;\n\
potential Voltage;\n\
flow Current;\n\
enddiscipline\n\
";
typedef lex::lexertl::token<frank::myIterator, boost::mpl::vector<frank::type::typePtr, std::string, double, int> > token_type;
typedef lex::lexertl::actor_lexer<token_type> lexer_type;
typedef frank::lexer<lexer_type>::iterator_type iterator_type;
typedef frank::fileParser<iterator_type, frank::lexer<lexer_type>::lexer_def> grammar_type;

frank::lexer<lexer_type> sLexer;
grammar_type sParser(sLexer);
frank::symbol::push_scope sPush;
auto pStringBegin = frank::myIterator(sInput);
auto pBegin(sLexer.begin(pStringBegin, frank::myIterator(sInput, true)));
const auto b = qi::phrase_parse(pBegin, sLexer.end(), sParser, qi::in_state("WS")[sLexer.self]);
}

最佳答案

Has anybody gotten this working for a lexer returning something else than double or int or string?



当然。可以找到简单的示例 on this site

And for the parser also returning non-trivial objects?



这是你真正的问题。 Spirit适用于在eDSL中轻松表达的解析器子集,并且具有“神奇地”映射到属性选择的巨大好处。

一些现实是:
  • 属性应具有值语义;使用多态属性很难(例如How can I use polymorphic attributes with boost::spirit::qi parsers?)
  • 由于所有“高级”解析器(例如 real_parser[u]int_parser)都在窗口之外,因此使用Lex的
  • 会使大多数最佳点消失了。根据记录,Spirit开发人员不愿意使用Lex。而且,Spirit X3不再支持Lex。


  • 背景资料:

    我非常考虑将源原样解析为直接的值类型的AST节点。我知道,这可能是您认为的“琐碎对象”,但不要被表面上的简单性所欺骗:递归变异树具有一定的表达能力。

    例子
  • 这是一个普通的AST,用于表示<20 LoC中的JSON:Boost Karma generator for composition of classes¹
  • 在这里,我们以全保真度表示Graphviz源格式:How to use boost spirit list operator with mandatory minimum amount of elements?

  • 此后,我创建了将AST转换为具有完全正确所有权的域表示形式的代码,并级联了词法范围内的节点/边缘属性和交叉引用。如果您感兴趣,我刚刚恢复了该工作和 put it up on github,主要是因为任务在许多方面都非常相似,例如覆盖/继承属性以及在范围内解析标识符: https://github.com/sehe/spirit-graphviz/blob/master/spirit-graphviz.cpp#L660

    建议,想法

    在您的情况下,我将采用类似的方法来保持简单性。所显示的代码(尚未)涵盖最棘手的要素(如学科中的自然属性替代)。

    一旦开始实现用例,例如解决给定节点上的兼容规则和绝对容差,您就需要具有完全保真度的域模型。最好不会丢失源信息和不变的AST信息²。

    作为中间立场,您可能会避免在内存中构建整个source-AST只是为了一次大的转变,而在顶层可以拥有:
    file = qi::skip(skipper) [
    *(m_sDiscipline | m_sNature | m_sModule) [process_ast(_1)]
    ];
    process_ast一次将“简单的” AST表示应用于域类型。这样,您只保留了少量的临时AST表示形式。

    域表示形式可以任意复杂,以支持您的所有逻辑和用例。

    让我们“展示,别说”

    烘焙与语法³相匹配的最简单的AST:
    namespace frank { namespace ast {
    struct nature {
    struct empty{};

    std::string name;
    std::string inherits;

    enum class Attribute { units, access, idt, ddt, abstol };
    using Value = boost::variant<int, double, std::string>;
    std::map<Attribute, Value> attributes;
    };

    struct discipline {
    enum enumDomain { eUnspecified, eDiscrete, eContinuous };
    struct properties_t {
    enumDomain domain = eUnspecified;
    boost::optional<std::string> flow, potential;
    };

    std::string name;
    properties_t properties;
    };

    // TODO
    using module = qi::unused_type;
    using file = std::vector<boost::variant<nature, discipline, module> >;

    enum class type { real, integer, string };
    } }

    这是微不足道的,并且将1:1映射到语法产生式上,这意味着我们几乎没有阻抗。

    代币?我们不需要Lex

    您可以拥有通用的 token 解析器,而无需Lex的复杂性

    Yes, Lex (especially statically generated) can potentially improve performance, but

    • if you need that, I wager Spirit Qi is not your best option anyways
    • premature optimization...


    我做了什么:
    struct tokens {
    // implicit lexemes
    qi::rule<It, std::string()> string, identifier;
    qi::rule<It, double()> real;
    qi::rule<It, int()> integer;
    qi::rule<It, ast::nature::Value()> value;
    qi::rule<It, ast::nature::Attribute()> attribute;
    qi::rule<It, ast::discipline::enumDomain()> domain;

    struct attribute_sym_t : qi::symbols<char, ast::nature::Attribute> {
    attribute_sym_t() {
    this->add
    ("units", ast::nature::Attribute::units)
    ("access", ast::nature::Attribute::access)
    ("idt_nature", ast::nature::Attribute::idt)
    ("ddt_nature", ast::nature::Attribute::ddt)
    ("abstol", ast::nature::Attribute::abstol);
    }
    } attribute_sym;

    struct domain_sym_t : qi::symbols<char, ast::discipline::enumDomain> {
    domain_sym_t() {
    this->add
    ("discrete", ast::discipline::eDiscrete)
    ("continuous", ast::discipline::eContinuous);
    }
    } domain_sym;

    tokens() {
    using namespace qi;
    auto kw = qr::distinct(copy(char_("a-zA-Z0-9_")));

    string = '"' >> *("\\" >> char_ | ~char_('"')) >> '"';
    identifier = char_("a-zA-Z_") >> *char_("a-zA-Z0-9_");
    real = double_;
    integer = int_;
    attribute = kw[attribute_sym];
    domain = kw[domain_sym];

    value = string | identifier | real | integer;

    BOOST_SPIRIT_DEBUG_NODES((string)(identifier)(real)(integer)(value)(domain)(attribute))
    }
    };

    解放,不是吗?注意如何
  • 所有属性都会自动传播
  • 字符串处理转义(此方法在Lex方法中已注释掉)。我们甚至不需要语义 Action (很难)撬出未引用/未转义的值
  • 我们使用distinct来确保关键字解析仅匹配完整标识符。 (请参阅How to parse reserved words correctly in boost spirit)。

    实际上,您会在这里注意到缺少单独的词法分析器。

    另一方面,这使上下文相关的关键字变得轻而易举(lex可以轻松地将关键字的优先级设置在关键字无法出现的位置的标识符的优先级。)

  • 跳过空间/评论呢?

    我们可以添加一个 token ,但是出于约定的原因,我将其设为解析器:
    struct skipParser : qi::grammar<It> {
    skipParser() : skipParser::base_type(spaceOrComment) {
    using namespace qi;
    spaceOrComment = space
    | ("//" >> *(char_ - eol) >> (eoi|eol))
    | ("/*" >> *(char_ - "*/") >> "*/");

    BOOST_SPIRIT_DEBUG_NODES((spaceOrComment))
    }
    private:
    qi::rule<It> spaceOrComment;
    };
    natureParser
    我们从 tokens继承了AST解析器:
    struct natureParser : tokens, qi::grammar<It, ast::nature(), skipParser> {

    从那里开始航行:
    property = attribute >> '=' >> value >> ';';

    nature
    = kw["nature"] >> identifier >> -(':' >> identifier) >> ';'
    >> *property
    >> kw["endnature"];
    disciplineParser
    discipline = kw["discipline"] >> identifier >> ';' 
    >> properties
    >> kw["enddiscipline"]
    ;

    properties
    = kw["domain"] >> domain >> ';'
    ^ kw["flow"] >> identifier >> ';'
    ^ kw["potential"] >> identifier >> ';'
    ;

    这显示了一种竞争方法,该方法使用置换运算符( ^)以任意顺序将可选的替代项解析为固定的 frank::ast::discipline属性结构。当然,您可能会选择在这里有一个更通用的表示形式,就像 ast::nature一样。

    Module AST is left as an exercise for the reader, though the parser rules are implemented below.



    顶层,封装了船长

    我讨厌必须从调用代码中指定船长(它比所需的要复杂,并且更改船长会更改语法)。因此,我将其封装在顶级解析器中:
    struct fileParser : qi::grammar<It, ast::file()> {
    fileParser() : fileParser::base_type(file) {
    file = qi::skip(qi::copy(m_sSkip)) [
    *(m_sDiscipline | m_sNature | m_sModule)
    ];

    BOOST_SPIRIT_DEBUG_NODES((file))
    }
    private:
    disciplineParser m_sDiscipline;
    natureParser m_sNature;
    moduleParser m_sModule;
    skipParser m_sSkip;

    qi::rule<It, ast::file()> file;
    };

    演示时间

    此演示为枚举添加了 operator<<,并添加了一个变体访问者以打印一些AST详细信息以进行调试/演示( print_em)。

    然后我们有一个测试驱动程序:
    int main() {
    using iterator_type = std::string::const_iterator;

    iterator_type iter = sInput.begin(), last = sInput.end();

    frank::Parsers<iterator_type>::fileParser parser;
    print_em print;

    frank::ast::file file;
    bool ok = qi::parse(iter, last, parser, file);

    if (ok) {
    for (auto& symbol : file)
    print(symbol);
    }
    else {
    std::cout << "Parse failed\n";
    }

    if (iter != last) {
    std::cout << "Remaining unparsed: '" << std::string(iter,last) << "'\n";
    }
    }

    使用您问题的样本输入,我们得到以下输出:

    Live On Coliru
    -- Nature
    name: Current
    inherits:
    attribute: units = A
    attribute: access = I
    attribute: idt = Charge
    attribute: abstol = 1e-12
    -- Nature
    name: Charge
    inherits:
    attribute: units = coul
    attribute: access = Q
    attribute: ddt = Current
    attribute: abstol = 1e-14
    -- Nature
    name: Voltage
    inherits:
    attribute: units = V
    attribute: access = V
    attribute: idt = Flux
    attribute: abstol = 1e-06
    -- Discipline
    name: electrical
    domain: (unspecified)
    flow: Current
    potential: Voltage
    Remaining unparsed: '
    '

    定义了 BOOST_SPIRIT_DEBUG后,您将获得丰富的调试信息: Live On Coliru

    完整 list

    Live On Coliru
    //#define BOOST_SPIRIT_DEBUG
    #include <map>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/fusion/include/adapted.hpp>
    #include <boost/spirit/repository/include/qi_distinct.hpp>

    namespace qi = boost::spirit::qi;

    namespace frank { namespace ast {
    struct nature {
    struct empty{};

    std::string name;
    std::string inherits;

    enum class Attribute { units, access, idt, ddt, abstol };
    using Value = boost::variant<int, double, std::string>;
    std::map<Attribute, Value> attributes;
    };

    struct discipline {
    enum enumDomain { eUnspecified, eDiscrete, eContinuous };
    struct properties_t {
    enumDomain domain = eUnspecified;
    boost::optional<std::string> flow, potential;
    };

    std::string name;
    properties_t properties;
    };

    // TODO
    using module = qi::unused_type;
    using file = std::vector<boost::variant<nature, discipline, module> >;

    enum class type { real, integer, string };
    } }

    BOOST_FUSION_ADAPT_STRUCT(frank::ast::nature, name, inherits, attributes)
    BOOST_FUSION_ADAPT_STRUCT(frank::ast::discipline, name, properties)
    BOOST_FUSION_ADAPT_STRUCT(frank::ast::discipline::properties_t, domain, flow, potential)

    namespace frank {
    namespace qr = boost::spirit::repository::qi;

    template <typename It> struct Parsers {

    struct tokens {
    // implicit lexemes
    qi::rule<It, std::string()> string, identifier;
    qi::rule<It, double()> real;
    qi::rule<It, int()> integer;
    qi::rule<It, ast::nature::Value()> value;
    qi::rule<It, ast::nature::Attribute()> attribute;
    qi::rule<It, ast::discipline::enumDomain()> domain;

    struct attribute_sym_t : qi::symbols<char, ast::nature::Attribute> {
    attribute_sym_t() {
    this->add
    ("units", ast::nature::Attribute::units)
    ("access", ast::nature::Attribute::access)
    ("idt_nature", ast::nature::Attribute::idt)
    ("ddt_nature", ast::nature::Attribute::ddt)
    ("abstol", ast::nature::Attribute::abstol);
    }
    } attribute_sym;

    struct domain_sym_t : qi::symbols<char, ast::discipline::enumDomain> {
    domain_sym_t() {
    this->add
    ("discrete", ast::discipline::eDiscrete)
    ("continuous", ast::discipline::eContinuous);
    }
    } domain_sym;

    tokens() {
    using namespace qi;
    auto kw = qr::distinct(copy(char_("a-zA-Z0-9_")));

    string = '"' >> *("\\" >> char_ | ~char_('"')) >> '"';
    identifier = char_("a-zA-Z_") >> *char_("a-zA-Z0-9_");
    real = double_;
    integer = int_;
    attribute = kw[attribute_sym];
    domain = kw[domain_sym];

    value = string | identifier | real | integer;

    BOOST_SPIRIT_DEBUG_NODES((string)(identifier)(real)(integer)(value)(domain)(attribute))
    }
    };

    struct skipParser : qi::grammar<It> {
    skipParser() : skipParser::base_type(spaceOrComment) {
    using namespace qi;
    spaceOrComment = space
    | ("//" >> *(char_ - eol) >> (eoi|eol))
    | ("/*" >> *(char_ - "*/") >> "*/");

    BOOST_SPIRIT_DEBUG_NODES((spaceOrComment))
    }
    private:
    qi::rule<It> spaceOrComment;
    };

    struct natureParser : tokens, qi::grammar<It, ast::nature(), skipParser> {
    natureParser() : natureParser::base_type(nature) {
    using namespace qi;
    auto kw = qr::distinct(copy(char_("a-zA-Z0-9_")));

    property = attribute >> '=' >> value >> ';';

    nature
    = kw["nature"] >> identifier >> -(':' >> identifier) >> ';'
    >> *property
    >> kw["endnature"];

    BOOST_SPIRIT_DEBUG_NODES((nature)(property))
    }
    private:
    using Attribute = std::pair<ast::nature::Attribute, ast::nature::Value>;

    qi::rule<It, ast::nature(), skipParser> nature;
    qi::rule<It, Attribute(), skipParser> property;

    using tokens::attribute;
    using tokens::value;
    using tokens::identifier;
    };

    struct disciplineParser : tokens, qi::grammar<It, ast::discipline(), skipParser> {
    disciplineParser() : disciplineParser::base_type(discipline) {

    auto kw = qr::distinct(qi::copy(qi::char_("a-zA-Z0-9_")));

    discipline = kw["discipline"] >> identifier >> ';'
    >> properties
    >> kw["enddiscipline"]
    ;

    properties
    = kw["domain"] >> domain >> ';'
    ^ kw["flow"] >> identifier >> ';'
    ^ kw["potential"] >> identifier >> ';'
    ;

    BOOST_SPIRIT_DEBUG_NODES((discipline)(properties))
    }
    private:
    qi::rule<It, ast::discipline(), skipParser> discipline;
    qi::rule<It, ast::discipline::properties_t(), skipParser> properties;

    using tokens::domain;
    using tokens::identifier;
    };

    struct moduleParser : tokens, qi::grammar<It, ast::module(), skipParser> {
    moduleParser() : moduleParser::base_type(module) {
    auto kw = qr::distinct(qi::copy(qi::char_("a-zA-Z0-9_")));

    m_sPort = identifier;
    m_sPortList = m_sPort % ',';
    m_sModulePortList = '(' >> m_sPortList >> ')';
    m_sModule = kw["module"];
    m_sType = kw["real"] | kw["integer"] | kw["string"];
    m_sParameter = kw["parameter"] >> m_sType >> identifier;
    m_sModuleItem = m_sParameter;
    m_sModuleItemList = *m_sModuleItem;
    module =
    (m_sModule >> identifier >> m_sModulePortList >> m_sModuleItemList >> kw["endmodule"]);
    }
    private:
    qi::rule<It, ast::module(), skipParser> module;
    qi::rule<It, skipParser> m_sModulePortList;
    qi::rule<It, skipParser> m_sPortList;
    qi::rule<It, skipParser> m_sPort;
    qi::rule<It, skipParser> m_sModule;

    qi::rule<It, skipParser> m_sModuleItemList;
    qi::rule<It, skipParser> m_sParameter;
    qi::rule<It, skipParser> m_sModuleItem;
    qi::rule<It, skipParser> m_sType;

    using tokens::identifier;
    };

    struct fileParser : qi::grammar<It, ast::file()> {
    fileParser() : fileParser::base_type(file) {
    file = qi::skip(qi::copy(m_sSkip)) [
    *(m_sDiscipline | m_sNature | m_sModule)
    ];

    BOOST_SPIRIT_DEBUG_NODES((file))
    }
    private:
    disciplineParser m_sDiscipline;
    natureParser m_sNature;
    moduleParser m_sModule;
    skipParser m_sSkip;

    qi::rule<It, ast::file()> file;
    };
    };

    }

    extern std::string const sInput;

    // just for demo
    #include <boost/optional/optional_io.hpp>

    namespace frank { namespace ast {
    //static inline std::ostream &operator<<(std::ostream &os, const nature::empty &) { return os; }
    static inline std::ostream &operator<<(std::ostream &os, nature::Attribute a) {
    switch(a) {
    case nature::Attribute::units: return os << "units";
    case nature::Attribute::access: return os << "access";
    case nature::Attribute::idt: return os << "idt";
    case nature::Attribute::ddt: return os << "ddt";
    case nature::Attribute::abstol: return os << "abstol";
    };
    return os << "?";
    }
    static inline std::ostream &operator<<(std::ostream &os, discipline::enumDomain d) {
    switch(d) {
    case discipline::eDiscrete: return os << "discrete";
    case discipline::eContinuous: return os << "continuous";
    case discipline::eUnspecified: return os << "(unspecified)";
    };
    return os << "?";
    }
    } }

    struct print_em {
    using result_type = void;
    template <typename V>
    void operator()(V const& variant) const {
    boost::apply_visitor(*this, variant);
    }
    void operator()(frank::ast::nature const& nature) const {
    std::cout << "-- Nature\n";
    std::cout << "name: " << nature.name << "\n";
    std::cout << "inherits: " << nature.inherits << "\n";
    for (auto& a : nature.attributes) {
    std::cout << "attribute: " << a.first << " = " << a.second << "\n";
    }
    }
    void operator()(frank::ast::discipline const& discipline) const {
    std::cout << "-- Discipline\n";
    std::cout << "name: " << discipline.name << "\n";
    std::cout << "domain: " << discipline.properties.domain << "\n";
    std::cout << "flow: " << discipline.properties.flow << "\n";
    std::cout << "potential: " << discipline.properties.potential << "\n";
    }
    void operator()(frank::ast::module const&) const {
    std::cout << "-- Module (TODO)\n";
    }
    };

    int main() {
    using iterator_type = std::string::const_iterator;

    iterator_type iter = sInput.begin(), last = sInput.end();

    frank::Parsers<iterator_type>::fileParser parser;
    print_em print;

    frank::ast::file file;
    bool ok = parse(iter, last, parser, file);

    if (ok) {
    for (auto& symbol : file)
    print(symbol);
    }
    else {
    std::cout << "Parse failed\n";
    }

    if (iter != last) {
    std::cout << "Remaining unparsed: '" << std::string(iter,last) << "'\n";
    }
    }

    std::string const sInput = R"(
    nature Current;
    units = "A";
    access = I;
    idt_nature = Charge;
    abstol = 1e-12;
    endnature

    // Charge in coulombs
    nature Charge;
    units = "coul";
    access = Q;
    ddt_nature = Current;
    abstol = 1e-14;
    endnature

    // Potential in volts
    nature Voltage;
    units = "V";
    access = V;
    idt_nature = Flux;
    abstol = 1e-6;
    endnature

    discipline electrical;
    potential Voltage;
    flow Current;
    enddiscipline
    )";

    ¹顺便说一句,那里的 other answer展示了具有多态属性和Spirit的“阻抗不匹配”-这次是在Karma方面

    ²(以防止依赖于评估顺序或类似事物的细微错误)

    ³(从 here中收集了一些信息,但未导入您的Lex方法未反射(reflect)的过多复杂性)

    ⁴(实际上,这是您需要在语法内部进行状态切换的地方,这是一个臭名昭著的开发区域,实际上在Spirit Lex中不可用:例如,当它工作 how to avoid defining token which matchs everything in boost::spirit::lex或当它表现不佳时: Boost.Spirit SQL grammar/lexer failure)

    关于c++ - 无法获得boost::spirit解析器和词法分析器,用于除std::string或int或double以外的 token 类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47191531/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com