我试图编写一个能够接受MATRIX.{variableName} = [1,2,3;4,5,6]
形式输入的解析器,其中矩阵的表示(在这种情况下为2x3矩阵)有点像MATLAB' s格式(分号表示新行)。
最初的想法是将输入保存在2d标准向量中,以便进一步处理数据。这是我第一次编写解析器,而且我对Spirit框架有点无能为力。
我当前(不那么直观)的解决方案是输入类似MATRIX (2,3) = [1,2,3,4,5,6]
来表示与上面相同的矩阵并将数据保存在一维向量中并利用行和列数据稍后进行处理(我相信有点像Eigen对动态矩阵的实现)。
namespace client
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;
namespace fusion = boost::fusion;
template <typename Iterator>
bool parse_matrix(Iterator first, Iterator last, unsigned& rows, unsigned& cols, std::vector<double>& vals)
{
using qi::double_;
using qi::uint_;
using qi::_1;
using qi::lit;
using qi::phrase_parse;
using ascii::space;
using phoenix::push_back;
double rN = 0.0;
double iN = 0.0;
unsigned i=0;
rows = 0, cols = 0;
bool r = phrase_parse(first, last,
// Begin grammar
(
lit("MATRIX") >> '(' >> uint_[phoenix::ref(rows) = _1] >> ',' >> uint_[phoenix::ref(cols) = _1] >> ')' >> '='
>> '[' >> double_[push_back(phoenix::ref(vals),_1)]
>> *(',' >> double_[push_back(phoenix::ref(vals),_1)]) >> ']'
// | double_[ref(rN) = _1]
),
// End grammar
space);
if (!r || first != last) // fail if we did not get a full match
return false;
if (vals.size() != (rows*cols))
return false;
// c = std::complex<double>(rN, iN);
return r;
}
}
我想也许有可能在解析某些字符(如分号)时调用std::vector<double>
这样的函数。std::vector<std::vector<double> >
。这可能吗?或者我如何实际实现我最初的想法?
答案 0 :(得分:1)
我建议:
不使用语义动作进行属性传播。您可以使用它来添加验证条件(请参阅 Boost Spirit: "Semantic actions are evil"? )
使用自动属性传播,因此您不必传递参考
在解析过程中不进行验证,除非您按原因这样做。
最小可行解析器然后变为:
<强> Live On Coliru 强>
#include <boost/spirit/include/qi.hpp>
using It = boost::spirit::istream_iterator;
using Row = std::vector<double>;
using Mat = std::vector<Row>;
int main() {
It f(std::cin>>std::noskipws), l;
Mat matrix;
std::string name;
{
using namespace boost::spirit::qi;
rule<It, std::string()> varname_ = char_("a-zA-Z_") >> *char_("a-zA-Z0-9_");
if (phrase_parse(f, l,
lit("MATRIX") >> '.' >> '{' >> varname_ >> '}' >> '='
>> '[' >> (int_ % ',' % ';') >> ']',
space, name, matrix))
{
std::cout << "Parsed: variabled named '" << name << "' [";
for(auto& row : matrix)
std::copy(row.begin(), row.end(), std::ostream_iterator<double>(std::cout<<"\n\t",", "));
std::cout << "\n]\n";
} else {
std::cout << "Parse failed\n";
}
}
if (f!=l)
std::cout << "Remaining input: '" << std::string(f,l) << "'\n";
}
可以看到为输入"MATRIX.{variable_name}=[1,2,3;4,5,6]"
打印以下输出:
Parsed: variabled named 'variable_name' [
1, 2, 3,
4, 5, 6,
]
如果您希望尽早捕获不一致的行长度,请参阅例如:这个答案:
答案 1 :(得分:0)
你需要将表达式分解为:
rows: DOUBLE | rows ',' DOUBLE
columns: rows | rows ';' rows
matrix: IDENTIFIER '.' '{' IDENTIFIER '}' '=' '[' columns ']'
现在,您的行可以有vector<double>
。列的vector< vector<double> >
,并将结果保存在矩阵中。
当然,当您在矩阵中获得列时,应检查所有行是否具有相同的大小。它不是必需的,但显然矩阵(例如[1,2;3,4,5]
)无效,即使语法允许它。