在x3中动态切换符号表 [英] Dynamically switching symbol tables in x3

查看:69
本文介绍了在x3中动态切换符号表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

鉴于以下正确解析的x3语法,我想添加参数,限定词和属性的验证.这似乎表明了一种在各种规则中动态切换正在使用哪个符号表的方法.实现此目的的最佳方法是什么?这似乎是语义动作和属性的某种混合,但我不清楚如何实现.

Given the following x3 grammar that parses correctly, I want to add validation of parameters, qualifiers, and properties. This would seem to indicate some method of dynamically switching which symbol table is being used within the various rules. What is the best way to implement this? It would seem to be some mixture of semantic actions and attributes, but it is not clear to me how.

#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

推荐答案

因此,我花了很多时间对此进行思考.

So, I spent quite some time thinking about this.

我承认,大多数想法都无法摆脱头脑风暴.但是,我从/just/最低的要求开始,从头开始做了概念验证:

I admit most of the thoughts didn't escape brainstorm. However, I made a proof-of-concept, from scratch, starting from /just/ the bare minimum:

/* Synopsis:
 *
 *    LOAD DLL <file-spec>
 *    LOAD PDB <file-spec>
 *    SET DEBUG {/ON | /OFF}
 *    SET TRACE {/ON | /OFF}
 *
 *    SHOW DEBUG
 *    SHOW MODULE <wildcard-expression> [/SYMBOLS]
 *    SHOW SYMBOLS { [/ALL] [/FULL] [/OUT=<file-spec>] [/TYPE=(+{all,exports,imports})] [wild-card-expression] }...
 *    SHOW TRACE
 */

实用程序

由于我们有多个域,这些域可以具有被视为(不区分大小写)关键字标识符的选项集,因此我想到了为这些对象创建便利的工具.

Utilities

Since we have several domains that can have sets of options that are to be treated as (case-insensitive) keyword identifiers, I thought of creating a facility for those:

注意:为简便起见,现在将所有值保留为int.因此,它没有达到更好的枚举" .但是只要有几个宏,您就应该能够将Options::type(和Enum<TagType>)解析为适当的枚举类型.

Note: for brevity this keeps all values as int for now. In that, it falls short of "Better Enum". But given a few macros you should be able to make Options::type (and Enum<TagType>) resolve to a proper enum type.

namespace util {
    template <typename Tag> struct FlavouredString : std::string {
        using std::string::string;
        using std::string::operator=;
    };

    template <typename Tag> struct Options {
        using type = int; // TODO typed enums? Requires macro tedium

        std::vector<char const*> _options;
        Options(std::initializer_list<char const*> options) : _options(options) {}
        Options(std::vector<char const*> options) : _options(options) {}

        std::string to_string(type id)              const { return _options.at(id); }
        type         to_id(std::string const& name) const { return find(_options.begin(), _options.end(), name) - _options.begin(); }
    };

    template <typename Tag> using Enum = typename Options<Tag>::type;
    template <typename Tag> struct IcOptions : Options<Tag> { using Options<Tag>::Options; };
}

为支持我们的AST类型,我们将创建以下实用程序的实例,例如:

To support our AST types, we will create instances of these utilities like:

IcOptions<struct DllPdb>  static const dll_pdb { "DLL",   "PDB"   };
IcOptions<struct Setting> static const setting { "DEBUG", "TRACE" };
IcOptions<struct OnOff>   static const on_off  { "OFF",   "ON"    };
IcOptions<struct SymType> static const sym_type{ "all", "imports", "exports" };

using Wildcard = FlavouredString<struct _Wild>;
using Filespec = FlavouredString<struct _Filespec>;

AST类型

这是一条与以前完全不同的路线:与其定义具有任意数量的任意类型的参数和值的通用AST,我选择定义强类型的命令:

AST Types

This goes a completely different route from before: instead of defining a general-purpose AST with arbitrary numbers of arbitrary-type arguments and values, I've opted to define the commands strongly-typed:

namespace ast {

    struct LoadCommand {
        Enum<DllPdb> kind = {};
        Filespec     filespec;
    };

    struct SetCommand {
        Enum<Setting> setting = {};
        Enum<OnOff> value     = {};
    };

    struct ShowSettingCommand {
        Enum<Setting> setting;
    };

    struct ShowModuleCommand {
        Wildcard wildcard;
        bool symbols = false;
    };

    using SymbolTypes = std::vector<Enum<SymType> >; 

    struct ShowSymbolsCommand {
        bool all  = false;
        bool full = false;
        Filespec out;
        SymbolTypes types;
        Wildcard wildcard;
    };

    using Command = boost::variant<
        LoadCommand,
        SetCommand,
        ShowSettingCommand,
        ShowModuleCommand,
        ShowSymbolsCommand
    >;
}

适应就像以前一样:

BOOST_FUSION_ADAPT_STRUCT(scl::ast::LoadCommand,        kind, filespec)
BOOST_FUSION_ADAPT_STRUCT(scl::ast::SetCommand,         setting, value)
BOOST_FUSION_ADAPT_STRUCT(scl::ast::ShowSettingCommand, setting)
BOOST_FUSION_ADAPT_STRUCT(scl::ast::ShowModuleCommand,  wildcard, symbols)

请注意,由于规则未遵循结构布局,因此ShowSymbolsCommand不适用

解析器实用程序

让我们通过一些可组合的解析器工厂来支持我们的核心概念:

Parser Utilities

Let's support our core concepts with some composable parser factories:

// (case insensitive) keyword handling
static auto kw        = [](auto p) { return x3::lexeme[p >> !(x3::graph - x3::char_("/=,()"))]; };
static auto ikw       = [](auto p) { return x3::no_case [kw(p)]; };
static auto qualifier = [](auto p) { return x3::lexeme['/' >> ikw(p)]; };

我可以解释这些,但是下面的用法会更清楚.因此,事不宜迟,请介绍一种技巧,使我们可以在解析器表达式中直接使用任何OptionsCiOptions实例:

I could explain these, but the usage below will be more clear. So, without further ado, presenting the trick that allows us to use any Options or CiOptions instance directly in a parser expression:

// Options and CiOptions
namespace util {
    template <typename Tag>
    auto as_spirit_parser(Options<Tag> const& o, bool to_lower = false) {
        x3::symbols<typename Options<Tag>::type> p;
        int n = 0;
        for (std::string el : o._options) {
            if (to_lower) boost::to_lower(el);
            p.add(el, n++);
        }
        return kw(p);
    }

    template <typename Tag>
    auto as_spirit_parser(IcOptions<Tag> const& o) {
        return x3::no_case [ as_spirit_parser(o, true) ];
    }
}

我想没有什么意外的,但是它确实允许优雅的规则定义:

Nothing unexpected there, I suppose, but it does allow for elegant rule definitions:

DEF_RULE(Filespec) = quoted_string | bare_string;
DEF_RULE(Wildcard) = lexeme[+char_("a-zA-Z0-9$_.\\*?+-")];

DEF_RULE(LoadCommand)
    = ikw("load") >> ast::dll_pdb >> Filespec;

DEF_RULE(SetCommand)
    = ikw("set") >> ast::setting >> qualifier(ast::on_off);

DEF_RULE(ShowSettingCommand)
    = ikw("show") >> ast::setting;

DEF_RULE(ShowModuleCommand)
    = ikw("show") >> ikw("module") >> Wildcard >> matches[qualifier("symbols")];

// ... ShowSymbolsQualifiers (see below) ...

DEF_RULE(ShowSymbolsCommand)
    = ikw("show") >> ikw("symbols") >> *ShowSymbolsQualifiers;

DEF_RULE(Command)
    = skip(blank)[ LoadCommand | SetCommand | ShowSettingCommand | ShowModuleCommand | ShowSymbolsCommand ];

重物起重

您会注意到我跳过了ShowSymbolsQualifiers.那是因为那是唯一不能从自动属性传播中受益的规则,所以我求助于使用语义动作:

The Heavier Lifting

You'll note I skipped ShowSymbolsQualifiers. That's because that's the only rule that cannot benefit from automatic attribute propagation, so I've resorted to using semantic actions:

请注意 IIFE习惯用法允许非常本地化的"助手定义

Note the IIFE idiom allows for "very local" helper definitions

DEF_RULE(SymbolTypes) = [] {
        auto type = as_parser(ast::sym_type);
        return '(' >> (type % ',') >> ')' | repeat(1) [ type ];
    }(); // IIFE pattern

RULE(ShowSymbolsQualifiers, ShowSymbolsCommand)
    = [] {
        auto set = [](auto member, auto p) {
            auto propagate = [member](auto& ctx) {
                traits::move_to(_attr(ctx), _val(ctx).*(member));
            };
            return as_parser(p)[propagate];
        };

        using T = ast::ShowSymbolsCommand;;
        return qualifier("all")  >> set(&T::all, attr(true))
             | qualifier("full") >> set(&T::full, attr(true))
             | qualifier("out")  >> set(&T::out, '=' >> Filespec)
             | qualifier("type") >> set(&T::types, '=' >> SymbolTypes)
             | set(&T::wildcard, Wildcard);
    }(); // IIFE pattern

完整演示

在Coliru上直播

//#define BOOST_SPIRIT_X3_DEBUG
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>

#include <boost/algorithm/string/case_conv.hpp> // to_lower
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/home/x3.hpp>

/* Synopsis:
 *
 *    LOAD DLL <file-spec>
 *    LOAD PDB <file-spec>
 *    SET DEBUG {/ON | /OFF}
 *    SET TRACE {/ON | /OFF}
 *
 *    SHOW DEBUG
 *    SHOW MODULE <wildcard-expression> [/SYMBOLS]
 *    SHOW SYMBOLS { [/ALL] [/FULL] [/OUT=<file-spec>] [/TYPE=(+{all,exports,imports})] [wild-card-expression] }...
 *    SHOW TRACE
 */

namespace scl {
    namespace util {
        template <typename Tag> struct FlavouredString : std::string {
            using std::string::string;
            using std::string::operator=;
        };

        template <typename Tag> struct Options {
            using type = int; // TODO typed enums? Requires macro tedium

            std::vector<char const*> _options;
            Options(std::initializer_list<char const*> options) : _options(options) {}
            Options(std::vector<char const*> options) : _options(options) {}

            std::string to_string(type id)              const { return _options.at(id); }
            type         to_id(std::string const& name) const { return find(_options.begin(), _options.end(), name) - _options.begin(); }
        };

        template <typename Tag> using Enum = typename Options<Tag>::type;
        template <typename Tag> struct IcOptions : Options<Tag> { using Options<Tag>::Options; };
    }

    namespace ast {

        using namespace util;
        IcOptions<struct DllPdb>  static const dll_pdb { "DLL",   "PDB"   };
        IcOptions<struct Setting> static const setting { "DEBUG", "TRACE" };
        IcOptions<struct OnOff>   static const on_off  { "OFF",   "ON"    };
        IcOptions<struct SymType> static const sym_type{ "all", "imports", "exports" };

        using Wildcard = FlavouredString<struct _Wild>;
        using Filespec = FlavouredString<struct _Filespec>;

        struct LoadCommand {
            Enum<DllPdb> kind = {};
            Filespec     filespec;
        };

        struct SetCommand {
            Enum<Setting> setting = {};
            Enum<OnOff> value     = {};
        };

        struct ShowSettingCommand {
            Enum<Setting> setting;
        };

        struct ShowModuleCommand {
            Wildcard wildcard;
            bool symbols = false;
        };

        using SymbolTypes = std::vector<Enum<SymType> >; 

        struct ShowSymbolsCommand {
            bool all  = false;
            bool full = false;
            Filespec out;
            SymbolTypes types;
            Wildcard wildcard;
        };

        using Command = boost::variant<
            LoadCommand,
            SetCommand,
            ShowSettingCommand,
            ShowModuleCommand,
            ShowSymbolsCommand
        >;
    }
}

#ifndef NDEBUG // for debug printing
namespace scl { namespace ast {
    static inline std::ostream &operator<<(std::ostream &os, Wildcard const &w) { return os << std::quoted(w); }
    static inline std::ostream &operator<<(std::ostream &os, Filespec const &s) { return os << std::quoted(s); }
    static inline std::ostream &operator<<(std::ostream &os, LoadCommand const &cmd) {
        return os << "LOAD " << dll_pdb.to_string(cmd.kind) << " " << cmd.filespec ;
    }
    static inline std::ostream &operator<<(std::ostream &os, SetCommand const &cmd) {
        return os << "SET " << setting.to_string(cmd.setting) << " /" << on_off.to_string(cmd.value);
    }
    static inline std::ostream &operator<<(std::ostream &os, ShowSettingCommand const &cmd) {
        return os << "SHOW " << setting.to_string(cmd.setting);
    }
    static inline std::ostream &operator<<(std::ostream &os, ShowModuleCommand const &cmd) {
        return os << "SHOW MODULE " << cmd.wildcard << (cmd.symbols?" /SYMBOLS":"");
    }
    static inline std::ostream &operator<<(std::ostream &os, ShowSymbolsCommand const &cmd) {
        os << "SHOW SYMBOLS";
        if (cmd.all)          os << " /ALL";
        if (cmd.full)         os << " /FULL";
        if (cmd.out.size())   os << " /OUT=" << cmd.out;
        if (cmd.types.size()) { 
            os << " /TYPE=(";
            bool first = true;
            for (auto type : cmd.types)
                os << (std::exchange(first, false)?"":",") << sym_type.to_string(type);
            os << ")";
        }
        return os << " " << cmd.wildcard;
    }
} } 
#endif

BOOST_FUSION_ADAPT_STRUCT(scl::ast::LoadCommand,        kind, filespec)
BOOST_FUSION_ADAPT_STRUCT(scl::ast::SetCommand,         setting, value)
BOOST_FUSION_ADAPT_STRUCT(scl::ast::ShowSettingCommand, setting)
BOOST_FUSION_ADAPT_STRUCT(scl::ast::ShowModuleCommand,  wildcard, symbols)

// Grammar for simple command language
namespace scl {
    namespace x3 = boost::spirit::x3;

    // (case insensitive) keyword handling
    static auto kw        = [](auto p) { return x3::lexeme[p >> !(x3::graph - x3::char_("/=,()"))]; };
    static auto ikw       = [](auto p) { return x3::no_case [kw(p)]; };
    static auto qualifier = [](auto p) { return x3::lexeme['/' >> ikw(p)]; };

    // Options and CiOptions
    namespace util {
        template <typename Tag>
        auto as_spirit_parser(Options<Tag> const& o, bool to_lower = false) {
            x3::symbols<typename Options<Tag>::type> p;
            int n = 0;
            for (std::string el : o._options) {
                if (to_lower) boost::to_lower(el);
                p.add(el, n++);
            }
            return kw(p);
        }

        template <typename Tag>
        auto as_spirit_parser(IcOptions<Tag> const& o) {
            return x3::no_case [ as_spirit_parser(o, true) ];
        }
    }

    // shorthand rule declarations
    #define RULE(name, Attr) static auto const name = x3::rule<struct _##Attr, ast::Attr>{#Attr}
    #define DEF_RULE(Attr) RULE(Attr, Attr)

    using namespace x3;

    auto const bare_string
        = lexeme[+char_("a-zA-Z0-9$_.\\*?+-")]; // bare string taken from old "param" rule

    auto const quoted_string 
        = lexeme['"' >> *(('\\' > char_) | ~char_('"')) >> '"'];

    DEF_RULE(Filespec) = quoted_string | bare_string;
    DEF_RULE(Wildcard) = lexeme[+char_("a-zA-Z0-9$_.\\*?+-")];

    DEF_RULE(LoadCommand)
        = ikw("load") >> ast::dll_pdb >> Filespec;

    DEF_RULE(SetCommand)
        = ikw("set") >> ast::setting >> qualifier(ast::on_off);

    DEF_RULE(ShowSettingCommand)
        = ikw("show") >> ast::setting;

    DEF_RULE(ShowModuleCommand)
        = ikw("show") >> ikw("module") >> Wildcard >> matches[qualifier("symbols")];

    // Note the IIFE idiom allows for "very local" helper definitions
    DEF_RULE(SymbolTypes) = [] {
            auto type = as_parser(ast::sym_type);
            return '(' >> (type % ',') >> ')' | repeat(1) [ type ];
        }(); // IIFE idiom

    RULE(ShowSymbolsQualifiers, ShowSymbolsCommand)
        = [] {
            auto set = [](auto member, auto p) {
                auto propagate = [member](auto& ctx) {
                    traits::move_to(_attr(ctx), _val(ctx).*(member));
                };
                return as_parser(p)[propagate];
            };

            using T = ast::ShowSymbolsCommand;;
            return qualifier("all")  >> set(&T::all, attr(true))
                 | qualifier("full") >> set(&T::full, attr(true))
                 | qualifier("out")  >> set(&T::out, '=' >> Filespec)
                 | qualifier("type") >> set(&T::types, '=' >> SymbolTypes)
                 | set(&T::wildcard, Wildcard);
        }(); // IIFE idiom

    DEF_RULE(ShowSymbolsCommand)
        = ikw("show") >> ikw("symbols") >> *ShowSymbolsQualifiers;

    DEF_RULE(Command)
        = skip(blank)[ LoadCommand | SetCommand | ShowSettingCommand | ShowModuleCommand | ShowSymbolsCommand ];

#undef DEF_RULE
#undef RULE
} // End namespace scl

int main() {
    for (std::string const str : {
        "load dll test.dll",
        "LOAD pdb \"test special.pdb\"",
        "LOAD pDb test.pdb",
        "set debug /on",
        "show debug",
        "SHOW module test.dll/symbols",
        "SHOW MODULE TEST.DLL /SYMBOLS",
        "SHOW module test.dll / symbols",
        "SHOW module test.dll",
        "show symbols/type=exports test*",
        "show symbols/type=(exports,imports) test*",
        "show symbols test.dll/type=(imports,exports)",
        "show symbols test.dll/tyPE=(imports,exports)",
        "show symbols s*/out=s.txt",
        "show symbols /all /full",
    }) {
        std::cout << " ======== " << std::quoted(str) << std::endl;

        auto b = str.begin(), e = str.end();
        scl::ast::Command cmd;

        if (parse(b, e, scl::Command, cmd))
            std::cout << " - Parsed: " << cmd << std::endl;
        if (b != e)
            std::cout << " - Remaining unparsed: " << std::quoted(std::string(b, e)) << std::endl;
    }
}

打印

 ======== "load dll test.dll"
 - Parsed: LOAD DLL "test.dll"
 ======== "LOAD pdb \"test special.pdb\""
 - Parsed: LOAD PDB "test special.pdb"
 ======== "LOAD pDb test.pdb"
 - Parsed: LOAD PDB "test.pdb"
 ======== "set debug /on"
 - Parsed: SET DEBUG /ON
 ======== "show debug"
 - Parsed: SHOW DEBUG
 ======== "SHOW module test.dll/symbols"
 - Parsed: SHOW MODULE "test.dll" /SYMBOLS
 ======== "SHOW MODULE TEST.DLL /SYMBOLS"
 - Parsed: SHOW MODULE "TEST.DLL" /SYMBOLS
 ======== "SHOW module test.dll / symbols"
 - Parsed: SHOW MODULE "test.dll"
 - Remaining unparsed: "/ symbols"
 ======== "SHOW module test.dll"
 - Parsed: SHOW MODULE "test.dll"
 ======== "show symbols/type=exports test*"
 - Parsed: SHOW SYMBOLS /TYPE=(exports) "test*"
 ======== "show symbols/type=(exports,imports) test*"
 - Parsed: SHOW SYMBOLS /TYPE=(exports,imports) "test*"
 ======== "show symbols test.dll/type=(imports,exports)"
 - Parsed: SHOW SYMBOLS /TYPE=(imports,exports) "test.dll"
 ======== "show symbols test.dll/tyPE=(imports,exports)"
 - Parsed: SHOW SYMBOLS /TYPE=(imports,exports) "test.dll"
 ======== "show symbols s*/out=s.txt"
 - Parsed: SHOW SYMBOLS /OUT="s.txt" "s*"
 ======== "show symbols /all /full"
 - Parsed: SHOW SYMBOLS /ALL /FULL ""

这篇关于在x3中动态切换符号表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆