Related
Buon giorno,
I have to parse something such as:
foo: 123
"bar": 456
The quotes should be removed if they are here. I tried:
((+x3::alnum) | ('"' >> (+x3::alnum) >> '"'))
But the parser actions for this are of type variant<string, string> ; is there a way to make it so that the parser understands that those two are equivalent, and for my action to only get a single std::string as argument in its call?
edit: minimal repro (live on godbolt: https://gcc.godbolt.org/z/GcE8Pj4r5) :
#include <boost/spirit/home/x3.hpp>
using namespace boost::spirit;
// action handlers
struct handlers {
void create_member(const std::string& str) { }
};
// rules
static const x3::rule<struct id_obj_mem> obj_mem = "obj_mem";
#define EVENT(e) ([](auto& ctx) { x3::get<handlers>(ctx).e(x3::_attr(ctx)); })
static const auto obj_mem_def = ((
((+x3::alnum) | ('"' >> (+x3::alnum) >> '"'))
>> ':' >> x3::lit("123"))[EVENT(create_member)] % ',');
BOOST_SPIRIT_DEFINE(obj_mem)
// execution
int main()
{
handlers r;
std::string str = "foo: 123";
auto first = str.begin();
auto last = str.end();
bool res = phrase_parse(
first,
last,
boost::spirit::x3::with<handlers>(r)[obj_mem_def],
boost::spirit::x3::ascii::space);
}
I too consider this a kind of defect. X3 is definitely less "friendly" in terms of the synthesized attribute types. I guess it's just a tacit side-effect of being more core-language oriented, where attribute assignment is effectively done via default "visitor" actions.
Although I understand the value of keeping the magic to a minimum, and staying close to "pure C++", I vastly prefer the Qi way of synthesizing attributes here. I believe it has proven a hard problem to fix, as this problem has been coming/going in some iterations of X3.
I've long decided to basically fix it myself with variations of this idiom:
template <typename T> struct as_type {
auto operator()(auto p) const { return x3::rule<struct Tag, T>{} = p; }
};
static constexpr as_type<std::string> as_string{};
Now I'd write that as:
auto quoted = '"' >> +x3::alnum >> '"';
auto name = as_string(+x3::alnum | quoted);
auto prop = (name >> ':' >> "123")[EVENT(create_member)] % ',';
That will compile no problem:
Live On Coliru
#include <boost/spirit/home/x3.hpp>
#include <iomanip>
#include <iostream>
namespace x3 = boost::spirit::x3;
struct handlers {
void create_member(std::string const& str) {
std::cerr << __FUNCTION__ << " " << std::quoted(str) << "\n";
}
};
namespace Parser {
#define EVENT(e) ([](auto& ctx) { get<handlers>(ctx).e(_attr(ctx)); })
template <typename T> struct as_type {
auto operator()(auto p) const { return x3::rule<struct Tag, T>{} = p; }
};
static constexpr as_type<std::string> as_string{};
auto quoted = '"' >> +x3::alnum >> '"';
auto name = as_string(+x3::alnum | quoted);
auto prop = (name >> ':' >> "123")[EVENT(create_member)] % ',';
auto grammar = x3::skip(x3::space)[prop];
} // namespace Parser
int main() {
handlers r;
std::string const str = "foo: 123";
auto first = str.begin(), last = str.end();
bool res = parse(first, last, x3::with<handlers>(r)[Parser::grammar]);
return res ? 1 : 0;
}
Prints
create_member "foo"
Interesting Links
Spirit X3, How to get attribute type to match rule type?
Combining rules at runtime and returning rules
spirit x3 cannot propagate attributes of type optional<vector>
etc.
I'd like to make a keyword parser that matches i.e. int, but does not match int in integer with eger left over. I use x3::symbols to get automatically get the parsed keyword represented as an enum value.
Minimal example:
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/utility/error_reporting.hpp>
namespace x3 = boost::spirit::x3;
enum class TypeKeyword { Int, Float, Bool };
struct TypeKeywordSymbolTable : x3::symbols<TypeKeyword> {
TypeKeywordSymbolTable()
{
add("float", TypeKeyword::Float)
("int", TypeKeyword::Int)
("bool", TypeKeyword::Bool);
}
};
const TypeKeywordSymbolTable type_keyword_symbol_table;
struct TypeKeywordRID {};
using TypeKeywordRule = x3::rule<TypeKeywordRID, TypeKeyword>;
const TypeKeywordRule type_keyword = "type_keyword";
const auto type_keyword_def = type_keyword_symbol_table;
BOOST_SPIRIT_DEFINE(type_keyword);
using Iterator = std::string_view::const_iterator;
/* Thrown when the parser has failed to parse the whole input stream. Contains
* the part of the input stream that has not been parsed. */
class LeftoverError : public std::runtime_error {
public:
LeftoverError(Iterator begin, Iterator end)
: std::runtime_error(std::string(begin, end))
{}
std::string_view get_leftover_data() const noexcept { return what(); }
};
template<typename Rule>
typename Rule::attribute_type parse(std::string_view input, const Rule& rule)
{
Iterator begin = input.begin();
Iterator end = input.end();
using ExpectationFailure = boost::spirit::x3::expectation_failure<Iterator>;
typename Rule::attribute_type result;
try {
bool r = x3::phrase_parse(begin, end, rule, x3::space, result);
if (r && begin == end) {
return result;
} else { // Occurs when the whole input stream has not been consumed.
throw LeftoverError(begin, end);
}
} catch (const ExpectationFailure& exc) {
throw LeftoverError(exc.where(), end);
}
}
int main()
{
// TypeKeyword::Bool is parsed and "ean" is leftover, but failed parse with
// "boolean" leftover is desired.
parse("boolean", type_keyword);
// TypeKeyword::Int is parsed and "eger" is leftover, but failed parse with
// "integer" leftover is desired.
parse("integer", type_keyword);
// TypeKeyword::Int is parsed successfully and this is the desired behavior.
parse("int", type_keyword);
}
Basicly, I want integer not to be recognized as a keyword with additional eger left to parse.
I morphed the test cases into self-describing expectations:
Live On Compiler Explorer
Prints:
FAIL "boolean" -> TypeKeyword::Bool (expected Leftover:"boolean")
FAIL "integer" -> TypeKeyword::Int (expected Leftover:"integer")
OK "int" -> TypeKeyword::Int
Now, the simplest, naive approach would be to make sure you parse till the eoi, by simply changing
auto actual = parse(input, Parser::type_keyword);
To
auto actual = parse(input, Parser::type_keyword >> x3::eoi);
And indeed the tests pass: Live
OK "boolean" -> Leftover:"boolean"
OK "integer" -> Leftover:"integer"
OK "int" -> TypeKeyword::Int
However, this fits the tests, but not the goal. Let's imagine a more involved grammar, where type identifier; is to be parsed:
auto identifier
= x3::rule<struct id_, Ast::Identifier> {"identifier"}
= x3::lexeme[x3::char_("a-zA-Z_") >> *x3::char_("a-zA-Z_0-9")];
auto type_keyword
= x3::rule<struct tk_, Ast::TypeKeyword> {"type_keyword"}
= type_;
auto declaration
= x3::rule<struct decl_, Ast::Declaration>{"declaration"}
= type_keyword >> identifier >> ';';
I'll leave the details for Compiler Explorer:
OK "boolean b;" -> Leftover:"boolean b;"
OK "integer i;" -> Leftover:"integer i;"
OK "int j;" -> (TypeKeyword::Int j)
Looks good. But what if we add some interesting tests:
{"flo at f;", LeftoverError("flo at f;")},
{"boolean;", LeftoverError("boolean;")},
It prints (Live)
OK "boolean b;" -> Leftover:"boolean b;"
OK "integer i;" -> Leftover:"integer i;"
OK "int j;" -> (TypeKeyword::Int j)
FAIL "boolean;" -> (TypeKeyword::Bool ean) (expected Leftover:"boolean;")
So, the test cases were lacking. Your prose description is actually closer:
I'd like to make a keyword parser that matches i.e. int, but does not match int in integer with eger left over
That correctly implies you want to check the lexeme inside the type_keyword rule. A naive try might be checking that no identifier character follows the type keyword:
auto type_keyword
= x3::rule<struct tk_, Ast::TypeKeyword> {"type_keyword"}
= type_ >> !identchar;
Where identchar was factored out of identifier like so:
auto identchar = x3::char_("a-zA-Z_0-9");
auto identifier
= x3::rule<struct id_, Ast::Identifier> {"identifier"}
= x3::lexeme[x3::char_("a-zA-Z_") >> *identchar];
However, this doesn't work. Can you see why (peeking allowed: https://godbolt.org/z/jb4zfhfWb)?
Our latest devious test case now passes (yay), but int j; is now rejected! If you think about it, it only makes sense, because you have spaced skipped.
The essential word I used a moment ago was lexeme: you want to treat some units as lexemes (and whitespace stops the lexeme. Or rather, whitespace isn't automatically skipped inside the lexeme¹). So, a fix would be:
auto type_keyword
// ...
= x3::lexeme[type_ >> !identchar];
(Note how I sneakily already included that on the identifier rule earlier)
Lo and behold (Live):
OK "boolean b;" -> Leftover:"boolean b;"
OK "integer i;" -> Leftover:"integer i;"
OK "int j;" -> (TypeKeyword::Int j)
OK "boolean;" -> Leftover:"boolean;"
Summarizing
This topic is a frequently recurring one, and it requires a solid understanding of skippers, lexemes first and foremost. Here are some other posts for inspiration:
Stop X3 symbols from matching substrings
parsing identifiers except keywords
Boost Spirit x3: parse delimited string Where I introduce a more general helper you might find useful:
auto kw = [](auto p) {
return x3::lexeme [ x3::as_parser(p) >> !x3::char_("a-zA-Z0-9_") ];
};
Stop X3 symbols from matching substrings
Dynamically switching symbol tables in x3
Good luck!
Complete Listing
Anti-Bitrot, the final listing:
#include <boost/fusion/adapted.hpp>
#include <boost/fusion/include/as_vector.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/spirit/home/x3.hpp>
#include <iomanip>
#include <iostream>
namespace x3 = boost::spirit::x3;
namespace Ast {
enum class TypeKeyword { Int, Float, Bool };
static std::ostream& operator<<(std::ostream& os, TypeKeyword tk) {
switch (tk) {
case TypeKeyword::Int: return os << "TypeKeyword::Int";
case TypeKeyword::Float: return os << "TypeKeyword::Float";
case TypeKeyword::Bool: return os << "TypeKeyword::Bool";
};
return os << "?";
}
using Identifier = std::string;
struct Declaration {
TypeKeyword type;
Identifier id;
bool operator==(Declaration const&) const = default;
};
} // namespace Ast
BOOST_FUSION_ADAPT_STRUCT(Ast::Declaration, type, id)
namespace Ast{
static std::ostream& operator<<(std::ostream& os, Ast::Declaration const& d) {
return os << boost::lexical_cast<std::string>(boost::fusion::as_vector(d));
}
} // namespace Ast
namespace Parser {
struct Type : x3::symbols<Ast::TypeKeyword> {
Type() {
add("float", Ast::TypeKeyword::Float) //
("int", Ast::TypeKeyword::Int) //
("bool", Ast::TypeKeyword::Bool); //
}
} const static type_;
auto identchar = x3::char_("a-zA-Z_0-9");
auto identifier
= x3::rule<struct id_, Ast::Identifier> {"identifier"}
= x3::lexeme[x3::char_("a-zA-Z_") >> *identchar];
auto type_keyword
= x3::rule<struct tk_, Ast::TypeKeyword> {"type_keyword"}
= x3::lexeme[type_ >> !identchar];
auto declaration
= x3::rule<struct decl_, Ast::Declaration>{"declaration"}
= type_keyword >> identifier >> ';';
} // namespace Parser
struct LeftoverError : std::runtime_error {
using std::runtime_error::runtime_error;
friend std::ostream& operator<<(std::ostream& os, LeftoverError const& e) {
return os << (std::string("Leftover:\"") + e.what() + "\"");
}
bool operator==(LeftoverError const& other) const {
return std::string_view(what()) == other.what();
}
};
template<typename T>
using Maybe = boost::variant<T, LeftoverError>;
template <typename Rule,
typename Attr = typename x3::traits::attribute_of<Rule, x3::unused_type>::type,
typename R = Maybe<Attr>>
R parse(std::string_view input, Rule const& rule) {
Attr result;
auto f = input.begin(), l = input.end();
return x3::phrase_parse(f, l, rule, x3::space, result)
? R(std::move(result))
: LeftoverError({f, l});
}
int main()
{
using namespace Ast;
struct {
std::string_view input;
Maybe<Declaration> expected;
} cases[] = {
{"boolean b;", LeftoverError("boolean b;")},
{"integer i;", LeftoverError("integer i;")},
{"int j;", Declaration{TypeKeyword::Int, "j"}},
{"boolean;", LeftoverError("boolean;")},
};
for (auto [input, expected] : cases) {
auto actual = parse(input, Parser::declaration >> x3::eoi);
bool ok = expected == actual;
std::cout << std::left << std::setw(6) << (ok ? "OK" : "FAIL")
<< std::setw(12) << std::quoted(input) << " -> "
<< std::setw(20) << actual;
if (not ok)
std::cout << " (expected " << expected << ")";
std::cout << "\n";
}
}
¹ see Boost spirit skipper issues
I am trying to create a parser using boost's spirit qi parser. It is parsing a string that contains three types of values. A constant, a variable, or a function. The functions can be nested inside of each other. The test string is f(a, b) = f(g(z, x), g(x, h(x)), c), where a-e are constants, f-r are functions, and s-z are variables. I successfully created a rule that can correctly parse the expression. The trouble arose when I changed the function parsing the rule into a grammar. There were several errors that I was able to fix. I almost got the grammar to parse the expression and turn it into an abstract syntax tree I created. However I got this error about a file contained in the boost library and I could not figure out where it is coming from because I don't understand the compiler message. I was following the example put up on the website for putting data from a parser to a struct using the employee example: http://www.boost.org/doc/libs/1_41_0/libs/spirit/example/qi/employee.cpp
main.cpp
#include "Parser.h"
#include "Term.h"
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>
#include <list>
using std::string;
using std::cout;
using std::endl;
int main()
{
cout << "Unification Algorithm" << endl << endl;
string phrase = "f(a, b) = f(g(z, x), g(x, h(x)), c)";
string::const_iterator itr = phrase.begin();
string::const_iterator last = phrase.end();
cout << phrase << endl;
// Parser grammar
Parser<string::const_iterator> g;
// Output data
Expression expression;
if (phrase_parse(itr, last, g, boost::spirit::ascii::space, expression))
{
cout << "Expression parsed." << endl;
}
else
{
cout << "Could not parse expression." << endl;
}
}
Parser.h
#ifndef _Parser_h_
#define _Parser_h_
#include "Term.h"
#include <boost/spirit/include/qi.hpp>
#include <vector>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
template <typename Iterator>
struct Parser : qi::grammar<Iterator, Expression(), ascii::space_type>
{
Parser() : Parser::base_type(expression)
{
using qi::char_;
const_char = char_("a-eA-E");
fn_char = char_("f-rF-R");
var_char = char_("s-zS-Z");
basic_fn = fn_char >> char_('(') >> (const_char | var_char) % char_(',') >> char_(')');
first_fn_wrapper = fn_char >> char_('(') >> (basic_fn | const_char | var_char) % char_(',') >> char_(')');
nested_fn = fn_char >> char_('(') >> (first_fn_wrapper | const_char | var_char) % char_(',') >> char_(')');
expression = nested_fn >> char_("=") >> nested_fn;
}
// Constant character a - e
qi::rule<Iterator, T_Cons, ascii::space_type> const_char;
// Function character f - r
qi::rule<Iterator, char(), ascii::space_type> fn_char;
// Variable character s - z
qi::rule<Iterator, T_Var, ascii::space_type> var_char;
// Allows for basic function parsing eg. f(x, y, z)
qi::rule<Iterator, T_Fn, ascii::space_type> basic_fn;
// Allows for single nested functions eg. f(g(x), y, z)
qi::rule<Iterator, T_Fn, ascii::space_type> first_fn_wrapper;
// Allows for fully nested functions eg. f(g(x, h(y)), z) and so on
qi::rule<Iterator, T_Fn, ascii::space_type> nested_fn;
// Full rule for a nested function expression
qi::rule<Iterator, Expression, ascii::space_type> expression;
};
#endif // _Parser_h_
Term.h
#ifndef _Term_h_
#define _Term_h_
#include <boost/fusion/include/adapt_struct.hpp>
#include <vector>
struct Term
{
char name;
};
BOOST_FUSION_ADAPT_STRUCT(Term, (char, name))
struct T_Cons : Term
{
};
BOOST_FUSION_ADAPT_STRUCT(T_Cons, (char, name))
struct T_Var : Term
{
};
BOOST_FUSION_ADAPT_STRUCT(T_Var, (char, name))
struct T_Fn : Term
{
std::vector<Term> * params;
T_Fn() { params = new std::vector<Term>(); }
~T_Fn() { delete params; }
};
BOOST_FUSION_ADAPT_STRUCT(T_Fn, (std::vector<Term>*, params))
struct Expression
{
Term lh_term;
Term rh_term;
};
BOOST_FUSION_ADAPT_STRUCT(Expression, (char, name) (Term, lh_term) (Term, rh_term))
#endif // _Term_h_
I cannot link the entire error message from the compiler because it is extremely long, but here are the last few. These are the compile errors that it gave:
boost_1_46_0\boost\mpl\assert.hpp|360|error: no matching function for call to 'assertion_failed(mpl_::failed************ (boost::spirit::qi::grammar<Iterator, T1, T2, T3, T4>::grammar(const boost::spirit::qi::rule<Iterator_, T1_, T2_, T3_, T4_>&, const string&) [with Iterator_ = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >; T1_ = Expression; T2_ = boost::proto::exprns_::expr<boost::proto::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::asci|
boost_1_46_0\boost\proto\extends.hpp|540|error: use of deleted function 'boost::proto::exprns_::expr<boost::proto::tag::terminal, boost::proto::argsns_::term<boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, Expression(), boost::proto::exprns_::expr<boost::proto::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type> > >, 0l>:|
boost_1_46_0\boost\proto\detail\expr0.hpp|165|error: no matching function for call to 'boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, Expression(), boost::proto::exprns_::expr<boost::proto::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type> >::reference()'|
UPDATE Showing a simplified parser with a a recursive ast parsing the sample expression shown
As always, the assertion message leads to exactly the problem:
// If you see the assertion below failing then the start rule
// passed to the constructor of the grammar is not compatible with
// the grammar (i.e. it uses different template parameters).
BOOST_SPIRIT_ASSERT_MSG(
(is_same<start_type, rule<Iterator_, T1_, T2_, T3_, T4_> >::value)
, incompatible_start_rule, (rule<Iterator_, T1_, T2_, T3_, T4_>));
So it tells you you should match the grammar with the start rule: you have
struct Parser : qi::grammar<Iterator, Expression(), ascii::space_type>
but
qi::rule<Iterator, Expression, ascii::space_type> expression;
Clearly you forgot parentheses there:
qi::rule<Iterator, Expression(), ascii::space_type> expression;
Guidelines when using generic libraries:
Some of these "rules" are generically applicable, with the exception of no. 2 which is specifically related to Boost Spirit:
baby steps; start small (empty, even)
start with the AST to match the grammar exactly
build gradually,
compiling every step along the way
UPDATE
Here's a much simplified grammar. As mentioned, in the "first rules of spirit" just before, start with the AST to match the grammar exactly:
namespace ast {
namespace tag {
struct constant;
struct variable;
struct function;
}
template <typename Tag> struct Identifier { char name; };
using Constant = Identifier<tag::constant>;
using Variable = Identifier<tag::variable>;
using Function = Identifier<tag::function>;
struct FunctionCall;
using Expression = boost::make_recursive_variant<
Constant,
Variable,
boost::recursive_wrapper<FunctionCall>
>::type;
struct FunctionCall {
Function function;
std::vector<Expression> params;
};
struct Equation {
Expression lhs, rhs;
};
}
Of course this could be much simpler still since all identifiers are just char and you could do the switching dynamically (impression).
Now, the grammar will have to follow. 1. Keep it simple 2. Format carefully 3. Match the ast directly, 4. add debug macros:
template <typename It, typename Skipper = ascii::space_type>
struct Parser : qi::grammar<It, ast::Equation(), Skipper>
{
Parser() : Parser::base_type(equation_)
{
using namespace qi;
constant_ = qi::eps >> char_("a-eA-E");
function_ = qi::eps >> char_("f-rF-R");
variable_ = qi::eps >> char_("s-zS-Z");
function_call = function_ >> '(' >> -(expression_ % ',') >> ')';
expression_ = constant_ | variable_ | function_call;
equation_ = expression_ >> '=' >> expression_;
BOOST_SPIRIT_DEBUG_NODES((constant_)(function_)(variable_)(function_call)(expression_)(equation_))
}
qi::rule<It, ast::Constant()> constant_;
qi::rule<It, ast::Function()> function_;
qi::rule<It, ast::Variable()> variable_;
qi::rule<It, ast::FunctionCall(), Skipper> function_call;
qi::rule<It, ast::Expression(), Skipper> expression_;
qi::rule<It, ast::Equation(), Skipper> equation_;
};
Note how the comments have become completely unneeded. Also note how recursively using expression_ solved your biggest headache!
Full Program
Live On Coliru
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace ast {
namespace tag {
struct constant;
struct variable;
struct function;
}
template <typename Tag> struct Identifier { char name; };
using Constant = Identifier<tag::constant>;
using Variable = Identifier<tag::variable>;
using Function = Identifier<tag::function>;
struct FunctionCall;
using Expression = boost::make_recursive_variant<
Constant,
Variable,
boost::recursive_wrapper<FunctionCall>
>::type;
struct FunctionCall {
Function function;
std::vector<Expression> params;
};
struct Equation {
Expression lhs, rhs;
};
}
BOOST_FUSION_ADAPT_STRUCT(ast::Constant, (char, name))
BOOST_FUSION_ADAPT_STRUCT(ast::Variable, (char, name))
BOOST_FUSION_ADAPT_STRUCT(ast::Function, (char, name))
BOOST_FUSION_ADAPT_STRUCT(ast::FunctionCall, (ast::Function, function)(std::vector<ast::Expression>, params))
BOOST_FUSION_ADAPT_STRUCT(ast::Equation, (ast::Expression, lhs)(ast::Expression, rhs))
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
template <typename It, typename Skipper = ascii::space_type>
struct Parser : qi::grammar<It, ast::Equation(), Skipper>
{
Parser() : Parser::base_type(equation_)
{
using namespace qi;
constant_ = qi::eps >> char_("a-eA-E");
function_ = qi::eps >> char_("f-rF-R");
variable_ = qi::eps >> char_("s-zS-Z");
function_call = function_ >> '(' >> -(expression_ % ',') >> ')';
expression_ = constant_ | variable_ | function_call;
equation_ = expression_ >> '=' >> expression_;
BOOST_SPIRIT_DEBUG_NODES((constant_)(function_)(variable_)(function_call)(expression_)(equation_))
}
qi::rule<It, ast::Constant()> constant_;
qi::rule<It, ast::Function()> function_;
qi::rule<It, ast::Variable()> variable_;
qi::rule<It, ast::FunctionCall(), Skipper> function_call;
qi::rule<It, ast::Expression(), Skipper> expression_;
qi::rule<It, ast::Equation(), Skipper> equation_;
};
int main() {
std::cout << "Unification Algorithm\n\n";
std::string const phrase = "f(a, b) = f(g(z, x), g(x, h(x)), c)";
using It = std::string::const_iterator;
It itr = phrase.begin(), last = phrase.end();
std::cout << phrase << std::endl;
Parser<It> g;
ast::Equation parsed;
if (phrase_parse(itr, last, g, ascii::space, parsed)) {
std::cout << "Expression parsed.\n";
} else {
std::cout << "Could not parse equation.\n";
}
if (itr != last) {
std::cout << "Remaining unparsed input: '" << std::string(itr,last) << "'\n";
}
}
A vanilla C++ solution (as per popular request)
I compiled it with MSVC 2013.
Lack of unrestricted unions support lead me to duplicate the 3 possible values of an argument.
There are workarounds for this limitation, but (like so many other things in C++) they are rather messy, so I kept them out to limit code obfuscation.
#include <string>
#include <vector>
#include <iostream>
using namespace std;
// possible token types
enum tTokenType {
T_CONST, // s-z
T_VAR, // a-e
T_FUNC, // f-r
T_EQUAL, // =
T_COMMA, // ,
T_OBRACE, // (
T_CBRACE, // )
T_SPACE, // ' ' or '\t'
T_ERROR, // anything but spaces
T_EOI // end of input
};
// tokens
struct tToken {
tTokenType _type; // lexical element type
char _value; // the actual const/var/func letter
size_t _index; // position in translation unit
static const string constants, variables, functions, spacing;
static const char * type_name[];
tToken(tTokenType t, size_t index) : _type(t), _value(0), _index(index) {}
static tTokenType type(char c)
{
if (constants.find(c) != string::npos) return T_CONST;
if (variables.find(c) != string::npos) return T_VAR;
if (functions.find(c) != string::npos) return T_FUNC;
if (spacing .find(c) != string::npos) return T_SPACE;
if (c == '=') return T_EQUAL;
if (c == ',') return T_COMMA;
if (c == '(') return T_OBRACE;
if (c == ')') return T_CBRACE;
return T_ERROR;
}
tToken(char c, size_t index) : _value(c), _index(index)
{
_type = type(c);
}
void croak(tTokenType type)
{
string err(_index - 1, '-');
cerr << err << "^ expecting " << type_name[(int)type] << "\n";
}
};
const string tToken::variables("abcde");
const string tToken::functions("fghijklmnopqr");
const string tToken::constants("stuvwxyz");
const string tToken::spacing (" \t");
const char * tToken::type_name[] = { "constant", "variable", "function", "=", ",", "(", ")", "space", "error", "end of input" };
// parser
class Parser {
friend class Compiler;
string _input; // remaining program input
size_t _index; // current token index (for error tracking)
void skip_spaces(void)
{
while (_input.length() != 0 && tToken::type(_input[0]) == T_SPACE) next();
}
void next(void)
{
_input.erase(0, 1);
_index++;
}
public:
void read (string program)
{
_input = program;
_index = 0;
skip_spaces();
}
tToken get(void)
{
tToken res = peek();
next();
skip_spaces();
return res;
}
tToken peek(void)
{
if (_input.length() == 0) return tToken(T_EOI, _index);
return tToken (_input[0], _index);
}
tToken accept(tTokenType type)
{
tToken t = get();
return (t._type == type) ? t : tToken (T_ERROR, _index-1);
}
bool consume(tTokenType type)
{
tToken t = get();
bool res = t._type == type;
if (!res) t.croak(type);
return res;
}
};
// syntactic elements
struct tSyntacticElement {
char name;
bool valid;
tSyntacticElement() : name('?'), valid(false) {}
tSyntacticElement(char c) : name(c), valid(false) {}
};
class tConstant : private tSyntacticElement {
friend class tArgument;
tConstant() {}
tConstant(tToken t) : tSyntacticElement(t._value) { }
};
class tVariable : private tSyntacticElement {
friend class tArgument;
tVariable() {}
tVariable(tToken t) : tSyntacticElement(t._value) { }
};
class tFunCall : private tSyntacticElement {
friend class Compiler;
friend class tProgram;
friend class tArgument;
vector<tArgument>params;
tFunCall() {}
tFunCall(tToken t) : tSyntacticElement(t._value) { }
void add_argument(tArgument a);
string dump(void);
};
class tArgument {
friend class Compiler;
friend class tProgram;
friend class tFunCall;
tTokenType type;
// MSVC 2013 does not support unrestricted unions, so for the
// sake of simplicity I'll leave it as 3 separate attributes
tConstant c;
tVariable v;
tFunCall f;
tArgument() {}
tArgument(tToken val) : type(val._type)
{
if (val._type == T_CONST) c = val;
if (val._type == T_VAR ) v = val;
}
tArgument(tFunCall f) : type(T_FUNC ), f(f) {}
string dump(void)
{
if (type == T_VAR) return string("$") + v.name;
if (type == T_CONST) return string("#") + c.name;
if (type == T_FUNC) return f.dump();
return "!";
}
};
class tProgram {
friend class Compiler;
tArgument left;
tArgument right;
bool valid;
string dump(void) { return left.dump() + " = " + right.dump(); }
};
// syntactic analyzer
void tFunCall::add_argument(tArgument a) { params.push_back(a); }
string tFunCall::dump(void)
{
string res(1, name);
res += '(';
// it's 2015 and still no implode() in C++...
for (size_t i = 0; i != params.size(); i++)
{
res += params[i].dump();
if (i != params.size() - 1) res += ',';
}
res += ')';
return res;
}
class Compiler {
Parser parser;
tProgram program;
tFunCall parse_function(void)
{
tToken f = parser.accept(T_FUNC);
tFunCall res (f);
parser.accept(T_OBRACE);
for (;;)
{
tArgument a = parse_argument();
res.add_argument(a);
tToken next = parser.get();
if (next._type == T_CBRACE) break;
if (next._type != T_COMMA) return res;
}
res.valid = true;
return res;
}
tArgument parse_argument(void)
{
tToken id = parser.peek();
if (id._type == T_FUNC) return parse_function();
id = parser.get();
if (id._type == T_CONST) return id;
if (id._type == T_VAR) return id;
return tArgument(tToken (T_ERROR, id._index));
}
public:
void analyze(string input)
{
parser.read(input);
cerr << input << "\n";
program.left = parse_argument();
program.valid &= parser.consume(T_EQUAL);
program.right = parse_argument();
program.valid &= parser.consume(T_EOI);
}
string dump(void)
{
return program.dump();
}
};
int main(int argc, char * argv[])
{
Compiler compiler;
// compiler.analyze("f(a, b) = f(g(z, x), g(x, h(x)), c)");
compiler.analyze(argv[1]);
cout << compiler.dump();
return 0;
}
Grammar
Given the rather terse problem definition, I invented a grammar that should at least match the test input:
program : argument = argument
argument: variable
| constant
| fun_call
fun_call: fun_name ( arg_list )
arg_list: argument
| argument , arg_list
Parsing
Given the simplicity of the syntax, parsing is pretty straightforward.
Each character is basically something valid, a space or something invalid.
Spaces are silently consumed, so that the analyzer only gets useful tokens to process.
Analyze
Since I'm doing this barehanded, I simply define a function for each grammatical rule (program, fun_call, arg_list, argument).
The grammar is predictive (can't remember how it's called in posh books, LL1 maybe?) and there are no arithmetic expressions so the code is relatively lightweight.
Error reporting
Bah, just the barest minimum, and I did not really test it.
Proper error handling can easily double code size (even with yacc), so I drew the line early.
Invalid characters will be replaced by "!", and some expected symbols will be pointed at in a semblance of vintage C compilers output.
There are absolutely no re-synchronization attempts, so a typo inside a function call (especially a braces imbalance) will likely cast the rest of the translation unit to the bin.
Using the hard earned syntactic tree
The mighty compiler manages to spit out an equivalent of the input.
Just to show that something was done beside trimming white spaces, variables are preceded by a '$' and constants by a '#' (showing a deplorable lack of imagination).
Sample output
ExpressionCompiler "f(a) = z"
f(a) = z
f($a) = #z
ExpressionCompiler "f(a) = f(c,z)"
f(a) = f(c,z)
f($a) = f($c,#z)
ExpressionCompiler "f(a, b) = f(g(z, x), g(x, h(x)), c)"
f(a, b) = f(g(z, x), g(x, h(x)), c)
f($a,$b) = f(g(#z,#x),g(#x,h(#x)),$c)
ExpressionCompiler "f(a, b) + f(g(z, x), g(x, h(x)), c)"
f(a, b) + f(g(z, x), g(x, h(x)), c)
-------^ expecting =
f($a,$b) = f(g(#z,#x),g(#x,h(#x)),$c)
ExpressionCompiler "f(A, b) = f(g(z, x), g(x, h(x)), c)"
f(A, b) = f(g(z, x), g(x, h(x)), c)
f(!,$b) = f(g(#z,#x),g(#x,h(#x)),$c)
ExpressionCompiler "f(a, b) = K(g(z, x), g(x, h(x)), c)"
f(a, b) = K(g(z, x), g(x, h(x)), c)
----------^ expecting end of input
f($a,$b) = !
Assuming I have the following rule expecting a string value but should convert it to an integer:
integer %=
attr_cast<int,std::string>(
lexeme[
-(char_('+') | char_('-')) >>
char_("1-9") >> *char_("0-9")
]
)
;
so I defined a transformation structure:
template <>
struct transform_attribute<int, std::string, qi::domain>
{
// input type
typedef std::string& type;
static std::string pre(int & d) {
return "";
}
static void post(int & val, const type attr) {
val = boost::lexical_cast<int>(attr);
}
static void fail(int &) {}
};
unfortunately it expects to convert the int reference of the left rule into a string reference (I removed the reference operator but it does not work).
On the Internet I saw examples working the other way int to string where they don't use references for the int. How can I define a proper transformation customly?
First off, you don't have to write this:
integer %= qi::int_; // Job Done
Next, you could write the qi::attr_cast<std::string>(...) easier and more efficient by doing as_string[ ... ].
Next, if you do want to parse ints the laborious way, try this:
bool ok = parse(f, l,
(matches['-'] | -lit('+') >> attr(false)) [ phx::ref(negative) = _1 ] >> // sign
eps [ _val = 0 ] >>
+digit [ _val *= 10, _val += (_1 - '0') ],
parsed);
See a live demo on Coliru testing for (u)int(8,16,32,64,max)_t on the whole range:
The qi::int_parser<> template used above (via qi::int_) is essentially a generalization of this approach, but more efficient.
Now, you can of course do the traits trick if you insist:
namespace boost { namespace spirit { namespace traits {
template <typename Int>
struct assign_to_attribute_from_value<
Int,
std::string,
typename std::enable_if<std::is_integral<Int>::value, void>::type // Enabler
>
{
static void call(std::string const& val, Int& attr) {
//std::cout << __PRETTY_FUNCTION__ << "('" << val << "')\n";
attr = boost::lexical_cast<Int>(val);
}
};
} } }
Now this would be shooting a fly with a canon. Never mind that boost::lexical_cast doesn't handle uint8_t and int8_t correctly for this purpose (treating them specially as char and unsigned char?), so I had to also hard code exception for these:
// boost lexical_cast does not usefully support `char` types as integrals... (SIC)
template <>
struct assign_to_attribute_from_value<signed char, std::string> {
static void call(std::string const& val, signed char& attr) {
int tmp;
assign_to_attribute_from_value<int, std::string>::call(val, tmp);
attr = static_cast<signed char>(tmp);
}
};
template <>
struct assign_to_attribute_from_value<unsigned char, std::string> {
static void call(std::string const& val, unsigned char& attr) {
unsigned int tmp;
assign_to_attribute_from_value<unsigned int, std::string>::call(val, tmp);
attr = static_cast<unsigned char>(tmp);
}
};
Now all the test cases passed with
Int parsed = 0;
bool ok = parse(f, l, as_string [ -char_("-+") >> +digit ], parsed);
See it Live On Coliru as well.
Now let me conclude with the only "sane" approach: don't reinvent the wheel
Int parsed = 0;
bool ok = qi::parse(f, l, qi::auto_, parsed);
Full Program Live On Coliru
#include <boost/spirit/include/qi.hpp>
template <typename Int>
void do_test() {
for (Int const testcase : { std::numeric_limits<Int>::min(), Int(), std::numeric_limits<Int>::max() }) {
auto const input = std::to_string(testcase);
auto f(input.begin()), l(input.end());
Int parsed = 0;
bool ok = boost::spirit::qi::parse(f, l, boost::spirit::qi::auto_, parsed);
if (!ok || f!=l)
throw std::runtime_error("parse error");
std::cout << std::boolalpha << (testcase==parsed) << "\t" << testcase << " -> " << parsed << "\n";
}
}
int main() {
do_test<int16_t>(); do_test<uint16_t>();
do_test<int32_t>(); do_test<uint32_t>();
do_test<int64_t>(); do_test<uint64_t>();
do_test<intmax_t>(); do_test<uintmax_t>();
}
I'm having trouble getting a small boost::spirit grammar to compile. It's a small part of a larger grammar that I'm having trouble with and I'm trying to test smaller parts to find my problem.
Basicly what this grammar should do is parse a double value which has any number of leading/trailing spaces. However when I try to compile I get a whole list of problems which I don't understand. Any help would be welcome!
The code:
grammar.h
#ifndef GRAMMAR_H
#define GRAMMAR_H
#include <boost/spirit/include/qi.hpp>
template <typename Iterator>
struct point_double_grammar : boost::spirit::qi::grammar<Iterator, double()>
{
/**
* Constructor used to create the grammar.
* #param is_point boolean indicating if the point is used as decimal.
* #author Luc Kleeven
**/
point_double_grammar() : point_double_grammar::base_type(d)
{
d = *boost::spirit::qi::lit(' ') >> boost::spirit::qi::double_ >> *boost::spirit::qi::lit(' ');
}
boost::spirit::qi::rule<Iterator, double()> d;
};
#endif // GRAMMAR_H
main.cpp
#include "grammar.h"
int main(int argc, char *argv[])
{
point_double_grammar<std::string::iterator> point_grammar();
bool result = false;
double d = 0.0;
std::string p1 = "575040.3400";
std::string p2 = "117380.1200";
std::string p3 = "-001.22916765";
std::string p4 = "063.39171738";
std::string p5 = "2.5";
std::string::iterator it;
std::string::iterator last;
it = p1.begin();
last = p1.end();
result = (boost::spirit::qi::parse(it, last, point_grammar, d) && it ==
last);
if(result)
{
std::cout << p1 << " == " << d << std::endl;
}
else
{
std::cout << "Parsing failed!" << std::endl;
}
it = p2.begin();
last = p2.end();
result = (boost::spirit::qi::parse(it, last, point_grammar, d) && it ==
last);
if(result)
{
std::cout << p2 << " == " << d << std::endl;
}
else
{
std::cout << "Parsing failed!" << std::endl;
}
it = p3.begin();
last = p3.end();
result = (boost::spirit::qi::parse(it, last, point_grammar, d) && it == last);
if(result)
{
std::cout << p3 << " == " << d << std::endl;
}
else
{
std::cout << "Parsing failed!" << std::endl;
}
it = p4.begin();
last = p4.end();
result = (boost::spirit::qi::parse(it, last, point_grammar, d) && it == last);
if(result)
{
std::cout << p4 << " == " << d << std::endl;
}
else
{
std::cout << "Parsing failed!" << std::endl;
}
it = p5.begin();
last = p5.end();
result = (boost::spirit::qi::parse(it, last, point_grammar, d) && it == last);
if(result)
{
std::cout << p5 << " == " << d << std::endl;
}
else
{
std::cout << "Parsing failed!" << std::endl;
}
return EXIT_SUCCESS;
}
When I try to compile I get the following errors:
In file included from c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/home/qi/detail/parse_auto.hpp:14:0,
from c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/home/qi/auto.hpp:16,
from c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/home/qi.hpp:15,
from c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/include/qi.hpp:16,
from ../test/grammar.h:4,
from ../test/main.cpp:1:
c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/home/qi/parse.hpp: In function 'bool boost::spirit::qi::parse(Iterator&, Iterator, const Expr&, Attr&) [with Iterator = __gnu_cxx::__normal_iterator<char*, std::basic_string<char> >, Expr = point_double_grammar<__gnu_cxx::__normal_iterator<char*, std::basic_string<char> > >(), Attr = double]':
../test/main.cpp:20:63: instantiated from here
c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/home/qi/parse.hpp:83:9: error: no matching function for call to 'assertion_failed(mpl_::failed************ (boost::spirit::qi::parse(Iterator&, Iterator, const Expr&, Attr&) [with Iterator = __gnu_cxx::__normal_iterator<char*, std::basic_string<char> >, Expr = point_double_grammar<__gnu_cxx::__normal_iterator<char*, std::basic_string<char> > >(), Attr = double]::error_invalid_expression::************)(point_double_grammar<__gnu_cxx::__normal_iterator<char*, std::basic_string<char> > > (*)()))'
c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/home/qi/parse.hpp:83:9: note: candidate is:
c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/mpl/assert.hpp:79:48: note: template<bool C> int mpl_::assertion_failed(typename mpl_::assert<C>::type)
In file included from c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/proto/detail/expr.hpp:6:0,
from c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/proto/expr.hpp:120,
from c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/proto/core.hpp:17,
from c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/proto/proto.hpp:12,
from c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/home/support/meta_compiler.hpp:19,
from c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/home/qi/meta_compiler.hpp:14,
from c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/home/qi/action/action.hpp:14,
from c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/home/qi/action.hpp:14,
from c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/home/qi.hpp:14,
from c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/include/qi.hpp:16,
from ../test/grammar.h:4,
from ../test/main.cpp:1:
c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/proto/detail/preprocessed/expr.hpp: At global scope:
c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/proto/detail/preprocessed/expr.hpp: In instantiation of 'boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<point_double_grammar<__gnu_cxx::__normal_iterator<char*, std::basic_string<char> > >()>, 0l>':
c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/utility/enable_if.hpp:59:10: instantiated from 'boost::disable_if<boost::proto::is_expr<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<point_double_grammar<__gnu_cxx::__normal_iterator<char*, std::basic_string<char> > >()>, 0l>, void>, void>'
c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/home/support/meta_compiler.hpp:212:16: instantiated from 'boost::spirit::result_of::compile<boost::spirit::qi::domain, point_double_grammar<__gnu_cxx::__normal_iterator<char*, std::basic_string<char> > >(), boost::spirit::unused_type, void>'
c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/home/qi/parse.hpp:86:82: instantiated from 'bool boost::spirit::qi::parse(Iterator&, Iterator, const Expr&, Attr&) [with Iterator = __gnu_cxx::__normal_iterator<char*, std::basic_string<char> >, Expr = point_double_grammar<__gnu_cxx::__normal_iterator<char*, std::basic_string<char> > >(), Attr = double]'
../test/main.cpp:20:63: instantiated from here
c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/proto/detail/preprocessed/expr.hpp:50:49: error: field 'boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<point_double_grammar<__gnu_cxx::__normal_iterator<char*, std::basic_string<char> > >()>, 0l>::child0' invalidly declared function type
In file included from c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/home/qi/detail/parse_auto.hpp:14:0,
from c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/home/qi/auto.hpp:16,
from c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/home/qi.hpp:15,
from c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/include/qi.hpp:16,
from ../test/grammar.h:4,
from ../test/main.cpp:1:
c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/home/qi/parse.hpp: In function 'bool boost::spirit::qi::parse(Iterator&, Iterator, const Expr&, Attr&) [with Iterator = __gnu_cxx::__normal_iterator<char*, std::basic_string<char> >, Expr = point_double_grammar<__gnu_cxx::__normal_iterator<char*, std::basic_string<char> > >(), Attr = double]':
../test/main.cpp:20:63: instantiated from here
c:\mingw\bin\../lib/gcc/mingw32/4.6.1/../../../../include/boost/spirit/home/qi/parse.hpp:86:82: error: request for member 'parse' in 'boost::spirit::compile [with Domain = boost::spirit::qi::domain, Expr = point_double_grammar<__gnu_cxx::__normal_iterator<char*, std::basic_string<char> > >(), typename boost::spirit::result_of::compile<Domain, Expr, boost::spirit::unused_type>::type = point_double_grammar<__gnu_cxx::__normal_iterator<char*, std::basic_string<char> > > (&)()]((* & expr))', which is of non-class type 'point_double_grammar<__gnu_cxx::__normal_iterator<char*, std::basic_string<char> > >()'
Note that I'm compiling using boost 1.48.0 and mingw 4.6.1 on a windows 7 machine.
Change this line:
point_double_grammar<std::string::iterator> point_grammar();
To:
point_double_grammar<std::string::iterator> point_grammar;
You've declared a function taking no arguments that returns a grammar. Instead, you want to instantiate the grammar.
Sharth has already answered your question, you should accept that answer of course.
I wanted to share a few other things, you might find helpful.
as I mentioned: format your code (code is for humans)
Try using a loop instead of duplicating your code. You weren't parsing a list of doubles. You were parsing a single double, 5 times
Avoid C-isms (declare and initialize at the top?)
Look at Qi Skippers: you were manually 'ignoring' whitespace. Qi has Skippers for that purpose (see qi::phrase_parse in below sample)
Consider using Boost Karma for output generation. At least use <iomanip> to control the output format (see std::setprecision below)
Here is the full example
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
template <typename Iterator, typename Skipper>
struct point_double_grammar : qi::grammar<Iterator, double(), Skipper>
{
point_double_grammar() : point_double_grammar::base_type(d)
{
d = qi::double_;
}
qi::rule<Iterator, double(), Skipper> d;
};
static const char *const testcases[] = {
"575040.3400",
"117380.1200",
"-001.22916765",
"063.39171738",
"2.5",
NULL
};
int main()
{
typedef std::string::const_iterator It;
point_double_grammar<It, qi::space_type> point_grammar;
for(const char* const* it=testcases; *it; ++it)
{
const std::string input(*it);
It it = input.begin(), last = input.end();
double d = 0.0;
bool result = (qi::phrase_parse(it, last, point_grammar, qi::space, d) && it ==
last);
if(result)
std::cout << input << " == "
<< std::setprecision(10) << d << std::endl;
else
std::cerr << "Parsing failed!" << std::endl;
}
}
With C++11 you'd even write:
for (const std::string input : {
"575040.3400", "117380.1200", "-001.22916765", "063.39171738", "2.5" })
{
auto it = input.begin(), last = input.end();
etc. For the record, the output is:
575040.3400 == 575040.34
117380.1200 == 117380.12
-001.22916765 == -1.22916765
063.39171738 == 63.39171738
2.5 == 2.5
Without std::setprecision(10) the output would be, e.g. 575040.3400 == 575040 for the first line. Consider using Boost Karma for output generation.