Re: Need to improve syntax error diagnostic message for Spirit grammar

"Mccall, Kurt E. \(MSFC-EV41\) via Spirit-general" <[email protected]> Thu, 24 Sep 2020 18:31:14 +0000
Newsgroups gmane.comp.parsers.spirit.general
Message-ID <SA9PR09MB5808E57596468C65A17609E1C6390@SA9PR09MB5808.namprd09.prod.outlook.com>
Seth,

I tried to imitate your example, but my code is still not pointing to the missing double quote in my input string.   The error message is:

error: line 3 col 23: Missing ending semicolon
         liftoff2  = "time > 0.35";
                      ^-- here
Parsing failed


Here is the self-contained example, hopefully less of a jigsaw puzzle.

************************************

#include <iostream>
#include <map>
#include <string>
#include <iomanip>

#include "/opt/boost_pgc/include/boost/spirit/include/qi.hpp"
#include "/opt/boost_pgc/include/boost/spirit/include/qi_expect.hpp"

#include "/opt/boost_pgc/include/boost/spirit/home/support/iterators/line_pos_iterator.hpp"
#include "/opt/boost_pgc/include/boost/locale.hpp"

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

using qi::eps;
using qi::lexeme;
using ascii::char_;
using ascii::space;

// for on_error
using qi::on_error;
using qi::fail;
using phoenix::val;

using std::cout;
using std::cerr;
using std::endl;
using std::string;

using Position_Itr_T = boost::spirit::line_pos_iterator<std::string::iterator>;

template <typename Iterator, typename Skipper>
    class Grammar4 : boost::spirit::qi::grammar<Iterator, Skipper>
{
    public:

    using rule_T     = boost::spirit::qi::rule<Iterator>;
    using rule_nil_T = boost::spirit::qi::rule<Iterator, Skipper>;
    using rule_str_T = boost::spirit::qi::rule<Iterator, string()>;

    // thanks for this idea!
    using diagnostics = std::map<std::string, std::string>;

    struct error_handler_impl
    {
        template <typename, typename, typename, typename, typename> struct result {
            typedef void type;
        };

        template <typename D, typename I>
        void operator()(D& _diagnostics, Iterator _begin,
                        Iterator _end, Iterator _where,
                        I const& _info) const {
            string const& tag(_info.tag);
            string const scratch = "Invalid syntax: expected " + tag;
            string const scratch2 = "Missing ";
            auto diagnostic(_diagnostics[tag]);

            if (diagnostic.empty())
                diagnostic = scratch;
            else
                diagnostic = scratch2 + diagnostic;


            // raise_parsing_error(diagnostic, _begin, _end, _where);
            auto bol  = get_line_start(_begin, _where);
            auto eol  = get_line_end(_where, _end);
            auto line = get_line(_where);
            auto col  = get_column(bol, _where);

            std::string subject(bol, eol);
            auto pos = std::distance(bol, _where);

            cerr << "error: line " << line << " col " << col << ": " << diagnostic << "\n";
            cerr << subject << "\n";
            cerr << std::setw(pos) << "" << "^-- here\n";
        }
    };

    diagnostics d0, d1, d2, d3;
    boost::phoenix::function<error_handler_impl> error_handler;

    rule_T comma, colon, semi, equals, keyword_events, dquote;
    rule_str_T ident, quoted_str;
    rule_nil_T start, event_list, event;

    Grammar4(void) : Grammar4::base_type{start}
    {
        comma  = ',';  comma.name(",");
        colon  = ':';  colon.name(":");
        semi   = ';';  semi.name(";");
        equals = '=';  equals.name("=");
        dquote = '"';  dquote.name("\"");

        keyword_events = "events";

        ident       = lexeme [ qi::raw [ (qi::alpha | '_') >> *(qi::alnum | '_') ] ];
        quoted_str  = lexeme [ dquote > +(char_ - '"') > dquote ];

        //d0 = {
        //    { dquote.name(), "double quote" }
        //};

        ident.name("ident");
        quoted_str.name("quoted_str");

        start.name("start");
        keyword_events.name("keyword_events");
        event_list.name("event_list");
        event.name("event");       

       start
            = eps
            > keyword_events
            > colon
            > event_list
            > semi
            > qi::eoi
        ;

        d1 = {
            { keyword_events.name(), "events keyword" },
            { colon.name(),                     "colon after events keyword" },
            { event_list.name(),              "event list",    },
            { semi.name(),                       "ending semicolon"    }
        };


        event_list
            = eps
            > event % comma
            ;

        d2 = {
            { event.name(),     "event definition" },
            { comma.name(),  "comma" }
        };

        event
            = eps
            > ident
            > equals
            > quoted_str
            ;

        d3 = {
            { ident.name(),           "identifier" },
            { equals.name(),         "equals sign" },
            { quoted_str.name(), "quoted C-expression string" }
        };

        //auto handler_bind0 = error_handler(phoenix::ref(d0), qi::_1, qi::_2, qi::_3, qi::_4);
        auto handler_bind1 = error_handler(phoenix::ref(d1), qi::_1, qi::_2, qi::_3, qi::_4);
        auto handler_bind2 = error_handler(phoenix::ref(d2), qi::_1, qi::_2, qi::_3, qi::_4);
        auto handler_bind3 = error_handler(phoenix::ref(d3), qi::_1, qi::_2, qi::_3, qi::_4);

        //on_error<fail>(quoted_str, handler_bind0);
        on_error<fail>(start, handler_bind1);
        on_error<fail>(event_list, handler_bind2);
        on_error<fail>(event, handler_bind3);
    }

    ~Grammar4(void) { };

    void parseInputFile(Iterator itr, Iterator itr_end)
    {
        try {
            bool r = phrase_parse(itr, itr_end, start, space);
            if (r) cout << "Parsing succeeded\n";
            else   cout << "Parsing failed\n";

        } catch (qi::expectation_failure<Position_Itr_T> const& ef)
        {
            cout << "Parsing failed2\n";
            //std::cout << "what 1: " << ef.what() << "\n";
            //std::cout << "what 2: " << ef.what_ << "\n";
        }
    }
};

int main(int argc, char **argv)
{
    boost::locale::generator gen;
    std::locale loc = gen("en_US.UTF-8");
    cout.imbue(loc);

    Grammar4<Position_Itr_T, boost::spirit::ascii::space_type> g;

    // SYNTAX ERROR: missing closing double quote after time > 0.25
    string input("events : \n \
        liftoff   = \"time > 0.25, \n \
        liftoff2  = \"time > 0.35\";");

    Position_Itr_T begin(input.begin());
    Position_Itr_T end(input.end());

    g.parseInputFile(begin, end);

    return 0;
};




_______________________________________________
Spirit-general mailing list
[email protected]
https://urldefense.proofpoint.com/v2/url?u=https-3A__lists.sourceforge.net_lists_listinfo_spirit-2Dgeneral&d=DwICAg&c=ApwzowJNAKKw3xye91w7BE1XMRKi2LN9kiMk5Csz9Zk&r=6cP1IfXu3IZOHSDh_vBqciYiIh4uuVgs1MSi5K7l5fQ&m=JQv3mClgVZPLRAmss85i3oF_jcnuGGJhiPqwdIMlHkk&s=_bKJ982Q1OiWqSD7sijWnuzIZN0HoBj2AinndrUuMLw&e= 


_______________________________________________
Spirit-general mailing list
[email protected]
https://urldefense.proofpoint.com/v2/url?u=https-3A__lists.sourceforge.net_lists_listinfo_spirit-2Dgeneral&d=DwICAg&c=ApwzowJNAKKw3xye91w7BE1XMRKi2LN9kiMk5Csz9Zk&r=6cP1IfXu3IZOHSDh_vBqciYiIh4uuVgs1MSi5K7l5fQ&m=MRjyf7v_iV5tkJSmWgAboOnAQQlSXc9uj9tZ3UN_FSs&s=cJPsFURy9_ClYFBDfvHj68wgV1xrWH476-9iAI0We10&e=