【发布时间】:2018-03-13 00:21:54
【问题描述】:
鉴于以下正确解析的 x3 语法,我想添加对参数、限定符和属性的验证。这似乎表明某种方法可以动态切换在各种规则中使用哪个符号表。实现这一点的最佳方法是什么?它似乎是语义动作和属性的某种混合,但我不清楚如何。
#include <string>
#include <vector>
#include <iostream>
#include <iomanip>
#include <map>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/variant.hpp>
#include <boost/fusion/adapted/struct.hpp>
namespace x3 = boost::spirit::x3;
namespace scl
{
//
// This will take a symbol value and return the string associated with that value. From an example by sehe
// TODO: There is probably a better C++14/17 way to do this with the symbol.for_each operator and a lambda,
// but I haven't figured it out yet
//
template <typename T>
struct SYMBOL_LOOKUP
{
SYMBOL_LOOKUP (T Symbol, std::string& String) : _sought (Symbol), _found (String)
{
}
void operator () (std::basic_string <char> s, T ct)
{
if (_sought == ct)
{
_found = s;
}
}
std::string found () const { return _found; }
private:
T _sought;
std::string& _found;
};
//
// This section describes the valid verbs, the parameters that are valid for each verb, and
// the qualifiers that are valid for each verb or parameter of a verb.
// TODO: There is probably some complicated C++11/14/17 expression template for generating all
// of this as a set of linked tables, where each verb points to a parameter table, which points
// to a qualifier table, but that is currently beyond my ability to implement, so each structure
// is implemented discretely
//
//
// Legal verbs
//
enum class VERBS
{
load, //
set, //
show, //
};
struct VALID_VERBS : x3::symbols <VERBS>
{
VALID_VERBS ()
{
add
("load", VERBS::load) //
("set", VERBS::set) //
("show", VERBS::show) //
;
}
} const valid_verbs;
//
// LOAD parameter 1
//
enum class LOAD_PARAMETER1
{
dll, // LOAD DLL <file-spec>
pdb, // LOAD PDB <file-spec>
};
struct VALID_LOAD_PARAMETER1 : x3::symbols <LOAD_PARAMETER1>
{
VALID_LOAD_PARAMETER1 ()
{
add
("dll", LOAD_PARAMETER1::dll) //
("pdb", LOAD_PARAMETER1::pdb) //
;
}
} const valid_load_parameter1;
//
// SET parameter 1
//
enum class SET_PARAMETER1
{
debug, // SET DEBUG {/ON | /OFF}
trace, // SET TRACE {/ON | OFF}
};
struct VALID_SET_PARAMETER1 : x3::symbols <SET_PARAMETER1>
{
VALID_SET_PARAMETER1 ()
{
add
("debug", SET_PARAMETER1::debug) //
("trace", SET_PARAMETER1::trace) //
;
}
} const valid_set_parameter1;
//
// SET qualifiers
//
enum class SET_QUALIFIERS
{
off, //
on //
};
struct VALID_SET_QUALIFIERS : x3::symbols <SET_QUALIFIERS>
{
VALID_SET_QUALIFIERS ()
{
add
("off", SET_QUALIFIERS::off) //
("on", SET_QUALIFIERS::on) //
;
}
} const valid_set_qualifiers;
//
// SHOW parameter 1
//
enum class SHOW_PARAMETER1
{
debug, // SHOW DEBUG
module, // SHOW MODULE <wildcard-expression> [/SYMBOLS]
symbols, // SHOW SYMBOLS *{/ALL /FULL /OUT=<file-spec> /TYPE=(+{all,exports,imports})} [wild-card-expression]
trace, // SHOW TRACE
};
struct VALID_SHOW_PARAMETER1 : x3::symbols <SHOW_PARAMETER1>
{
VALID_SHOW_PARAMETER1 ()
{
add
("debug", SHOW_PARAMETER1::debug) //
("module", SHOW_PARAMETER1::module) //
("symbols", SHOW_PARAMETER1::symbols) //
("trace", SHOW_PARAMETER1::trace) //
;
}
} const valid_show_parameter1;
//
// SHOW qualifiers
//
enum class SHOW_QUALIFIERS
{
all, // Display all objects of the specified type
full, // Display all information about the specified object(s)
out, // Write output to the specified file (/out=<file spec>)
type, // List of properties to display
};
struct VALID_SHOW_QUALIFIERS : x3::symbols <SHOW_QUALIFIERS>
{
VALID_SHOW_QUALIFIERS ()
{
add
("all", SHOW_QUALIFIERS::all) //
("full", SHOW_QUALIFIERS::full) //
("out", SHOW_QUALIFIERS::out) //
("type", SHOW_QUALIFIERS::type) // Valid properties in VALID_SHOW_TYPE_PROPERTIES
;
}
} const valid_show_qualifiers;
//
// SHOW /TYPE=(property_list)
//
enum class SHOW_TYPE_PROPERTIES
{
all, //
exports, //
imports, //
};
struct VALID_SHOW_TYPE_PROPERTIES : x3::symbols <SHOW_TYPE_PROPERTIES>
{
VALID_SHOW_TYPE_PROPERTIES ()
{
add
("all", SHOW_TYPE_PROPERTIES::all) //
("exports", SHOW_TYPE_PROPERTIES::exports) //
("imports", SHOW_TYPE_PROPERTIES::imports) //
;
}
} const valid_show_type_properties;
//
// Convert a verb value to its string representation
//
std::string to_string (const VERBS Verb)
{
std::string result;
SYMBOL_LOOKUP <VERBS> lookup (Verb, result);
//
// Loop through all the entries in the symbol table looking for the specified value
// Is there a better way to use this for_each with a lambda?
//
valid_verbs.for_each (lookup);
return result;
} // End to_string
} // End namespace scl
namespace scl_ast
{
struct KEYWORD : std::string
{
using std::string::string;
using std::string::operator=;
};
struct NIL
{
};
using VALUE = boost::variant <NIL, std::string, int, double, KEYWORD>;
struct PROPERTY
{
KEYWORD name;
VALUE value;
};
struct QUALIFIER
{
enum KIND
{
positive,
negative
} kind;
std::string identifier;
std::vector <PROPERTY> properties;
};
struct PARAMETER
{
KEYWORD keyword;
std::vector <QUALIFIER> qualifiers;
};
struct COMMAND
{
scl::VERBS verb;
std::vector <QUALIFIER> qualifiers;
std::vector <PARAMETER> parameters;
};
//
// Overloads for printing the AST to the console
//
#pragma region debug
static inline std::ostream& operator<< (std::ostream& os, VALUE const& v)
{
struct
{
std::ostream& _os;
void operator() (std::string const& s) const { _os << std::quoted (s); }
void operator() (int i) const { _os << i; }
void operator() (double d) const { _os << d; }
void operator() (KEYWORD const& kwv) const { _os << kwv; }
void operator() (NIL) const { }
} vis { os };
boost::apply_visitor (vis, v);
return os;
}
static inline std::ostream& operator<< (std::ostream& os, PROPERTY const& prop)
{
os << prop.name;
if (prop.value.which ())
{
os << "=" << prop.value;
}
return os;
}
static inline std::ostream& operator<< (std::ostream& os, QUALIFIER const& q)
{
os << "/" << (q.kind == QUALIFIER::negative ? "no" : "") << q.identifier;
if (!q.properties.empty ())
{
os << "=(";
}
for (auto const& prop : q.properties)
{
os << prop << " ";
}
if (!q.properties.empty ())
{
os << ")";
}
return os;
}
static inline std::ostream& operator<< (std::ostream& os, std::vector <QUALIFIER> const& qualifiers)
{
for (auto const& qualifier : qualifiers)
{
os << " " << qualifier;
}
return os;
}
static inline std::ostream& operator<< (std::ostream& os, PARAMETER const& p)
{
return os << p.keyword << " " << p.qualifiers;
}
static inline std::ostream& operator<< (std::ostream& os, COMMAND const& cmd)
{
os << scl::to_string (cmd.verb) << cmd.qualifiers;
for (auto& param : cmd.parameters)
{
os << " " << param;
}
return os;
}
#pragma endregion debug
}; // End namespace scl_ast
BOOST_FUSION_ADAPT_STRUCT (scl_ast::PROPERTY, name, value);
BOOST_FUSION_ADAPT_STRUCT (scl_ast::QUALIFIER, kind, identifier, properties);
BOOST_FUSION_ADAPT_STRUCT (scl_ast::PARAMETER, keyword, qualifiers);
BOOST_FUSION_ADAPT_STRUCT (scl_ast::COMMAND, verb, qualifiers, parameters);
//
// Grammar for simple command language
//
namespace scl
{
using namespace x3;
auto const param = rule <struct _keyword, scl_ast::KEYWORD> { "param" }
= lexeme [+char_ ("a-zA-Z0-9$_.\\*?+-")];
auto const identifier
= lexeme [+char_ ("a-zA-Z0-9_")];
auto const quoted_string
= lexeme ['"' >> *('\\' > char_ | ~char_ ('"')) >> '"'];
auto const property_value
= quoted_string
| real_parser <double, x3::strict_real_policies <double>> {}
| int_
| param;
auto const property = rule <struct _property, scl_ast::PROPERTY> { "property" }
= identifier >> -('=' >> property_value);
auto const property_list = rule <struct _property_list, std::vector <scl_ast::PROPERTY>> { "property_list" }
= '(' >> property % ',' >> ')';
auto const qual
= attr (scl_ast::QUALIFIER::positive) >> lexeme ['/' >> identifier] >> -( '=' >> (property_list | repeat (1) [property]));
auto const neg_qual
= attr (scl_ast::QUALIFIER::negative) >> lexeme [no_case ["/no"] >> identifier] >> repeat (0) [property]; // Negated qualifiers never have properties (repeat(0) keeps the compiler happy)
auto const qualifier
= neg_qual | qual;
auto const verb
= no_case [valid_verbs]; // Uses static list of allowed verbs
auto const parameter = rule <struct _parameter, scl_ast::PARAMETER> { "parameter" }
= param >> *qualifier;
auto const command = rule <struct _command, scl_ast::COMMAND> { "command" }
= skip (blank) [verb >> *qualifier >> *parameter];
}; // End namespace scl
int
main ()
{
std::vector <std::string> input =
{
"load dll test.dll",
"LOAD pdb test.pdb",
"set debug /on",
"show debug",
"SHOW module test.dll/symbols",
"show symbols/type=export test*",
"show symbols test.dll/type=(import,export)",
"show symbols s*/out=s.txt",
"show symbols /all /full",
};
for (auto const& str : input)
{
scl_ast::COMMAND cmd;
auto b = str.begin ();
auto e = str.end ();
bool ok = parse (b, e, scl::command, cmd);
std::cout << (ok ? "OK" : "FAIL") << '\t' << std::quoted (str) << std::endl;
if (ok)
{
std::cout << " -- Full AST: " << cmd << std::endl;
std::cout << " -- Verb + Qualifiers: " << scl::to_string (cmd.verb) << cmd.qualifiers << std::endl;
for (auto const& param : cmd.parameters)
{
std::cout << " -- Parameter + Qualifiers: " << param << std::endl;
}
if (b != e)
{
std::cout << "*** Remaining unparsed: " << std::quoted (std::string (b, e)) << std::endl;
}
}
std::cout << std::endl;
} // End for
return 0;
} // End main
【问题讨论】:
-
你可以接受
COMMAND是每个动词的不同类型的变体吗?我会考虑一下。现在here's the symbol lookup,这是better enums 的东西,这是pattern that I would consider in Qi。我认为这个技巧在 X3 中没有意义,因此我的第一个问题 :) -
@sehe 当然,我不拘泥于任何特定的数据结构格式
-
太棒了。我明天会重温这个。到了睡觉时间。
-
@sehe 感谢您的链接。我一直在寻找一种更好的方法来处理枚举,但我找不到完整的解决方案; BetterEnums 看起来很棒
标签: c++ boost c++14 boost-spirit boost-spirit-x3