【问题标题】:How to propagate binary operators in boost spirit x3?How to propagate binary operators in boost spirit x3?
【发布时间】:2022-12-01 19:10:35
【问题描述】:

I had recently with the help of the amazing sehe managed to advance my boost spirit x3 parser for hlsl (high level shading language) that is a c-like language for writing shader kernels for GPU's. Here is the rough grammar I am following... https://craftinginterpreters.com/appendix-i.html

Here is the previous question and answer for the curious.

Trying to parse nested expressions with boost spirit x3

I am now trying to implement unary and binary operators and have hit a stumbling block with how they recurse. I am able to get it to compile and a single binary operator is parsed, but having multiple nested ones doesn't seem to be working. I suspect the solution is going to be involving semantic actions again to manually propagate values but I struggle to see how to do that yet as the side effects are hard to understand (still working out how it all works).

Here's my compiling example...

#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/spirit/home/x3/support/utility/error_reporting.hpp>
#include <iomanip>
#include <iostream>

namespace x3 = boost::spirit::x3;

namespace hlsl
{
    namespace ast
    {
        struct Void
        {
        };
        struct Get;
        struct Set;
        struct Call;
        struct Assign;
        struct Binary;
        struct Unary;

        struct Variable
        {
            std::string name;
        };

        using Expr = x3::variant<Void, x3::forward_ast<Get>, x3::forward_ast<Set>, Variable, x3::forward_ast<Call>, x3::forward_ast<Assign>, x3::forward_ast<Binary>, x3::forward_ast<Unary>>;

        struct Call
        {
            Expr name;
            std::vector<Expr> arguments_;
        };

        struct Get
        {
            Expr object_;
            std::string property_;
        };

        struct Set
        {
            Expr object_;
            Expr value_;
            std::string name_;
        };
        struct Assign
        {
            std::string name_;
            Expr value_;
        };

        struct Binary
        {
            Expr left_;
            std::string op_;
            Expr right_;
        };

        struct Unary
        {
            std::string op_;
            Expr expr_;
        };
    } // namespace ast

    struct printer
    {
        std::ostream &_os;
        using result_type = void;

        void operator()(hlsl::ast::Get const &get) const
        {
            _os << "get { object_:";
            get.object_.apply_visitor(*this);
            _os << ", property_:" << quoted(get.property_) << " }";
        }

        void operator()(hlsl::ast::Set const &set) const
        {
            _os << "set { object_:";
            set.object_.apply_visitor(*this);
            _os << ", name_:" << quoted(set.name_);
            _os << " equals: ";
            set.value_.apply_visitor(*this);
            _os << " }";
        }

        void operator()(hlsl::ast::Assign const &assign) const
        {
            _os << "assign { ";
            _os << "name_:" << quoted(assign.name_);
            _os << ", value_:";
            assign.value_.apply_visitor(*this);
            _os << " }";
        }

        void operator()(hlsl::ast::Variable const &var) const
        {
            _os << "var{" << quoted(var.name) << "}";
        };
        void operator()(hlsl::ast::Binary const &bin) const
        {
            _os << "binary { ";
            bin.left_.apply_visitor(*this);
            _os << " " << quoted(bin.op_) << " ";
            bin.right_.apply_visitor(*this);
            _os << " }";
        };

        void operator()(hlsl::ast::Unary const &un) const
        {
            _os << "unary { ";
            un.expr_.apply_visitor(*this);
            _os << quoted(un.op_);
            _os << " }";
        };
        void operator()(hlsl::ast::Call const &call) const
        {
            _os << "call{";
            call.name.apply_visitor(*this);
            _os << ", args: ";

            for (auto &arg : call.arguments_)
            {
                arg.apply_visitor(*this);
                _os << ", ";
            }
            _os << /*quoted(call.name) << */ "}";
        };
        void operator()(hlsl::ast::Void const &) const { _os << "void{}"; };
    };

} // namespace hlsl

BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Variable, name)
BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Call, name, arguments_)
BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Get, object_, property_)
BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Set, object_, value_)
BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Assign, name_, value_)
BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Binary, left_, op_, right_)
BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Unary, op_, expr_)

namespace hlsl::parser
{
    struct eh_tag;

    struct error_handler
    {
        template <typename It, typename Exc, typename Ctx>
        auto on_error(It &, It, Exc const &x, Ctx const &context) const
        {
            x3::get<eh_tag>(context)( //
                x.where(), "Error! Expecting: " + x.which() + " here:");

            return x3::error_handler_result::fail;
        }
    };

    struct program_ : error_handler
    {
    };

    x3::rule<struct identifier_, std::string> const identifier{"identifier"};
    x3::rule<struct variable_, ast::Variable> const variable{"variable"};
    x3::rule<struct arguments_, std::vector<ast::Expr>> const arguments{"arguments_"};
    x3::rule<struct binary_, hlsl::ast::Binary, true> const binary{"binary"};
    x3::rule<struct unary_, hlsl::ast::Unary> const unary{"unary"};
    x3::rule<struct unarycallwrapper_, hlsl::ast::Expr> const unarycallwrapper{"unarycallwrapper"};
    x3::rule<struct get_, ast::Expr> const get{"get"};
    x3::rule<struct call_, ast::Expr> const call{"call"};
    x3::rule<struct program_, ast::Expr> const program{"program"};
    x3::rule<struct primary_, ast::Expr> const primary{"primary"};
    x3::rule<struct expression_, ast::Expr> const expression{"expression"};
    x3::rule<struct set_, ast::Set, true> const set{"set"};
    x3::rule<struct assign_, ast::Assign> const assign{"assign"};
    x3::rule<struct assignment_, ast::Expr> const assignment{"assignment"};

    auto get_string_from_variable = [](auto &ctx)
    { _val(ctx).name_ = std::move(_attr(ctx).name); };

    auto fix_assignExpr = [](auto &ctx)
    { _val(ctx).value_ = std::move(_attr(ctx)); };

    auto as_expr = [](auto &ctx)
    { _val(ctx) = ast::Expr(std::move(_attr(ctx))); };

    auto as_unary = [](auto &ctx)
    { _val(ctx) = ast::Unary(std::move(_attr(ctx))); };

    auto as_call = [](auto &ctx)
    { _val(ctx) = ast::Call{std::move(_val(ctx)), std::move(_attr(ctx))}; };

    auto fold_in_get_to_set = [](auto &ctx)
    {
        auto &val = x3::_val(ctx);
        val.name_ = boost::get<x3::forward_ast<ast::Get>>(val.object_).get().property_;
        val.object_ = ast::Expr(boost::get<x3::forward_ast<ast::Get>>(val.object_).get().object_);
    };

    auto as_string = [](auto &ctx)
    { _val(ctx) = std::move(_attr(ctx).name); };
    auto as_assign = [](auto &ctx)
    { _val(ctx) = ast::Assign(std::move(_val(ctx)), std::move(_attr(ctx))); };
    auto as_get = [](auto &ctx)
    {
        _val(ctx) = ast::Get{std::move(_val(ctx)), _attr(ctx)};
    };

    auto variable_def = identifier;
    auto primary_def = variable;
    auto identifier_def = x3::lexeme[x3::alpha >> *x3::alnum];

    auto expression_def = assignment;
    auto assignment_def = (assign | set) | binary;  // replace binary with call to see the rest working
    auto assign_def = variable[get_string_from_variable] >> '=' >> assignment[fix_assignExpr];
    auto set_def = (get >> '=' >> assignment)[fold_in_get_to_set];

    auto arguments_def = *(expression % ',');
    auto get_def = primary[as_expr] >> *('.' >> identifier)[as_get];
    auto call_def = primary[as_expr] >> *((x3::lit('(') >> arguments >> x3::lit(')'))[as_call] | ('.' >> identifier)[as_get]);

    auto unary_def = (x3::string("-") >> unary);
    auto unarycallwrapper_def =  unary | call ;
    auto binary_def = unarycallwrapper >> x3::string("*") >> unarycallwrapper;

    auto program_def = x3::skip(x3::space)[expression];

    BOOST_SPIRIT_DEFINE(primary, assign, binary, unary, unarycallwrapper, assignment, get, set, variable, arguments, expression, call, identifier, program);

} // namespace hlsl::parser

int main()
{
    using namespace hlsl;

    for (std::string const input :
         {
             "first",
             "first.second",
             "first.Second.third",
             "first.Second().third",
             "first.Second(arg1).third",
             "first.Second(arg1, arg2).third",
             "first = second",
             "first.second = third",
             "first.second.third = fourth",
             "first.second.third = fourth()",
             "first.second.third = fourth(arg1)",
             "this * that", //binary { var{"this"} "*" var{"that"} }
             "this * -that", // binary { var{"this"} "*" unary{'-', var{"that"}} }
             "this * that * there", 
         }) //
    {
        std::cout << "===== " << quoted(input) << "\n";
        auto f = input.begin(), l = input.end();

        // Our error handler
        auto const p = x3::with<parser::eh_tag>(
            x3::error_handler{f, l, std::cerr})[hlsl::parser::program];

        if (hlsl::ast::Expr fs; parse(f, l, p, fs))
        {
            fs.apply_visitor(hlsl::printer{std::cout << "Parsed: "});
            std::cout << "\n";
        }
        else
        {
            std::cout << "Parse failed at " << quoted(std::string(f, l)) << "\n";
        }
    }
}

Any help is appreciated :)

【问题讨论】:

  • I was able to get the unary in the "this * -that" case to work by changing the... auto unary_def = (x3::string("-") &gt;&gt; unary); to auto unary_def = (x3::string("-") &gt;&gt; unarycallwrapper); I'm also aware now that the first of the 3 terms in the binary parser should be evaluated to a unary and returned as an expression of the second two terms fail. I'm not sure how to get the 2nd and third term in the binary parser to be optional and still be synthesized into the Binary Attriubute on success. again I suspect some semantic action magic. Oh @sehe please save me :)
  • The other thing I am thinking is that I probably don't need to store the binary op string as a string and could just use a lit and encode it into separate div and mult binary types. Then I could use the same semantic action machinery shown in the previous answer to nest multiple binary expressions. but there might be another way too.
  • Yeah semantic action magic is required to stay close to the grammar productions

标签: c++ boost boost-spirit boost-spirit-x3


【解决方案1】:

Here is how I have solved the issue.

Instead of having a single binary ast node that stores a string of either "*" or "/", I split it up into separate ast node types for divide and multiply.

I then used the same machinery suggested by @sehe in the linked answer to synthesize the right nodes.

I'm still unsure how you can use semantic actions to synthesize attributes that span accross multiple '>>' operators. I'm guessing that the _val(ctx) in the semantic action refers to the whole ast::Expr across the currently defined rule so maybe you can set one member of a ast::Binary (eg the op string from the x3::string("*"), then in the next term after the '>>' you write _val(ctx) again (copy construct from previous?) and set the next member from the _attr(ctx)? I'll see if I can investigate if that works next. That would allow some more complex synthesizing of Attributes. Although I'm not sure if you could have different types being set accross the rule.

#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/spirit/home/x3/support/utility/error_reporting.hpp>
#include <iomanip>
#include <iostream>

namespace x3 = boost::spirit::x3;

namespace hlsl
{
    namespace ast
    {
        struct Void
        {
        };
        struct Get;
        struct Set;
        struct Call;
        struct Assign;
        struct Divide;
        struct Multiply;
        struct Unary;

        struct Variable
        {
            std::string name;
            // operator std::string() const {
            //     return name;
            // }
        };

        using Expr = x3::variant<Void, x3::forward_ast<Get>, x3::forward_ast<Set>, Variable, x3::forward_ast<Call>, x3::forward_ast<Assign>, x3::forward_ast<Multiply>, x3::forward_ast<Divide>, x3::forward_ast<Unary>>;

        struct Call
        {
            Expr name;
            std::vector<Expr> arguments_;
        };

        struct Get
        {
            Expr object_;
            std::string property_;
        };

        struct Set
        {
            Expr object_;
            Expr value_;
            std::string name_;
        };
        struct Assign
        {
            std::string name_;
            Expr value_;
        };
        // struct Logical
        // {
        //     Expr left_;
        //     std::string op_;
        //     Expr right_;
        // };

        struct Multiply
        {
            Expr left_;
            Expr right_;
        };

        struct Divide
        {
            Expr left_;
            Expr right_;
        };

        struct Unary
        {
            std::string op_;
            Expr expr_;
        };
    } // namespace ast

    struct printer
    {
        std::ostream &_os;
        using result_type = void;

        void operator()(hlsl::ast::Get const &get) const
        {
            _os << "get { object_:";
            get.object_.apply_visitor(*this);
            _os << ", property_:" << quoted(get.property_) << " }";
        }

        void operator()(hlsl::ast::Set const &set) const
        {
            _os << "set { object_:";
            set.object_.apply_visitor(*this);
            _os << ", name_:" << quoted(set.name_);
            _os << " equals: ";
            set.value_.apply_visitor(*this);
            _os << " }";
        }

        void operator()(hlsl::ast::Assign const &assign) const
        {
            _os << "assign { ";
            _os << "name_:" << quoted(assign.name_);
            _os << ", value_:";
            assign.value_.apply_visitor(*this);
            _os << " }";
        }

        void operator()(hlsl::ast::Variable const &var) const
        {
            _os << "var{" << quoted(var.name) << "}";
        };
        void operator()(hlsl::ast::Divide const &bin) const
        {
            _os << "divide { ";
            bin.left_.apply_visitor(*this);
            bin.right_.apply_visitor(*this);
            _os << " }";
        };
        void operator()(hlsl::ast::Multiply const &bin) const
        {
            _os << "multiply { ";
            bin.left_.apply_visitor(*this);
            bin.right_.apply_visitor(*this);
            _os << " }";
        };

        void operator()(hlsl::ast::Unary const &un) const
        {
            _os << "unary { ";
            un.expr_.apply_visitor(*this);
            _os << quoted(un.op_);
            _os << " }";
        };
        void operator()(hlsl::ast::Call const &call) const
        {
            _os << "call{";
            call.name.apply_visitor(*this);
            _os << ", args: ";

            for (auto &arg : call.arguments_)
            {
                arg.apply_visitor(*this);
                _os << ", ";
            }
            _os << /*quoted(call.name) << */ "}";
        };
        void operator()(hlsl::ast::Void const &) const { _os << "void{}"; };
    };

} // namespace hlsl

BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Variable, name)
BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Call, name, arguments_)
BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Get, object_, property_)
BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Set, object_, value_)
BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Assign, name_, value_)
BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Multiply, left_, right_)
BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Divide, left_, right_)
BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Unary, op_, expr_)

namespace hlsl::parser
{
    struct eh_tag;

    struct error_handler
    {
        template <typename It, typename Exc, typename Ctx>
        auto on_error(It &, It, Exc const &x, Ctx const &context) const
        {
            x3::get<eh_tag>(context)( //
                x.where(), "Error! Expecting: " + x.which() + " here:");

            return x3::error_handler_result::fail;
        }
    };

    struct program_ : error_handler
    {
    };

    x3::rule<struct identifier_, std::string> const identifier{"identifier"};
    x3::rule<struct variable_, ast::Variable> const variable{"variable"};
    x3::rule<struct arguments_, std::vector<ast::Expr>> const arguments{"arguments_"};
    x3::rule<struct binary_, hlsl::ast::Expr> const binary{"binary"};
    x3::rule<struct multiply_, hlsl::ast::Expr> const multiply{"multiply"};
    x3::rule<struct divide_, hlsl::ast::Expr> const divide{"divide"};

    x3::rule<struct unary_, hlsl::ast::Unary> const unary{"unary"};
    x3::rule<struct unarycallwrapper_, hlsl::ast::Expr> const unarycallwrapper{"unarycallwrapper"};
    x3::rule<struct get_, ast::Expr> const get{"get"};
    x3::rule<struct call_, ast::Expr> const call{"call"};
    x3::rule<struct program_, ast::Expr> const program{"program"};
    x3::rule<struct primary_, ast::Expr> const primary{"primary"};
    x3::rule<struct expression_, ast::Expr> const expression{"expression"};
    x3::rule<struct set_, ast::Set, true> const set{"set"};
    x3::rule<struct assign_, ast::Assign> const assign{"assign"};
    x3::rule<struct assignment_, ast::Expr> const assignment{"assignment"};

    auto get_string_from_variable = [](auto &ctx)
    { _val(ctx).name_ = std::move(_attr(ctx).name); };

    auto fix_assignExpr = [](auto &ctx)
    { _val(ctx).value_ = std::move(_attr(ctx)); };

    auto as_expr = [](auto &ctx)
    { _val(ctx) = ast::Expr(std::move(_attr(ctx))); };

    auto as_unary = [](auto &ctx)
    { _val(ctx) = ast::Unary(std::move(_attr(ctx))); };

    auto as_call = [](auto &ctx)
    { _val(ctx) = ast::Call{std::move(_val(ctx)), std::move(_attr(ctx))}; };

    auto as_multiply = [](auto &ctx)
    { _val(ctx) = ast::Multiply{std::move(_val(ctx)), std::move(_attr(ctx))}; };

    auto as_divide = [](auto &ctx)
    { _val(ctx) = ast::Divide{std::move(_val(ctx)), std::move(_attr(ctx))}; };

    auto fold_in_get_to_set = [](auto &ctx)
    {
        auto &val = x3::_val(ctx);
        val.name_ = boost::get<x3::forward_ast<ast::Get>>(val.object_).get().property_;
        val.object_ = ast::Expr(boost::get<x3::forward_ast<ast::Get>>(val.object_).get().object_);
    };

    auto as_string = [](auto &ctx)
    { _val(ctx) = std::move(_attr(ctx).name); };
    auto as_assign = [](auto &ctx)
    { _val(ctx) = ast::Assign(std::move(_val(ctx)), std::move(_attr(ctx))); };
    auto as_get = [](auto &ctx)
    {
        _val(ctx) = ast::Get{std::move(_val(ctx)), _attr(ctx)};
    };

    auto variable_def = identifier;
    auto primary_def = variable;
    auto identifier_def = x3::lexeme[x3::alpha >> *x3::alnum];

    auto expression_def = assignment;
    auto assignment_def = (assign | set) | binary; // replace binary with call to see the rest working
    auto assign_def = variable[get_string_from_variable] >> '=' >> assignment[fix_assignExpr];
    auto set_def = (get >> '=' >> assignment)[fold_in_get_to_set];

    auto arguments_def = *(expression % ',');
    auto get_def = primary[as_expr] >> *('.' >> identifier)[as_get];
    auto call_def = primary[as_expr] >> *((x3::lit('(') >> arguments >> x3::lit(')'))[as_call] | ('.' >> identifier)[as_get]);

    auto unary_def = (x3::string("-") >> unarycallwrapper);
    auto unarycallwrapper_def = call | unary;
    auto binary_def =  unarycallwrapper[as_expr] >> *((x3::lit('/') >> unarycallwrapper[as_divide]) | (x3::lit('*') >> unarycallwrapper[as_multiply]));
    auto program_def = x3::skip(x3::space)[expression];

    BOOST_SPIRIT_DEFINE(primary, assign, binary, multiply, divide,  unary, unarycallwrapper, assignment, get, set, variable, arguments, expression, call, identifier, program);

} // namespace hlsl::parser

int main()
{
    using namespace hlsl;

    for (std::string const input :
         {
            "first",
            "first.second",
            "first.Second.third",
            "first.Second().third",
            "first.Second(arg1).third",
            "first.Second(arg1, arg2).third",
            "first = second",
            "first.second = third",
            "first.second.third = fourth",
            "first.second.third = fourth()",
            "first.second.third = fourth(arg1)",
            "this * that",  // binary { var{"this"} "*" var{"that"} }
            "this * -that", // binary { var{"this"} "*" unary{'-', var{"that"}} }
            "this * that * there",
            "this * that / there",
            "this.inner * that * there.inner2",
         }) //
    {
        std::cout << "===== " << quoted(input) << "
";
        auto f = input.begin(), l = input.end();

        // Our error handler
        auto const p = x3::with<parser::eh_tag>(
            x3::error_handler{f, l, std::cerr})[hlsl::parser::program];

        if (hlsl::ast::Expr fs; parse(f, l, p, fs))
        {
            fs.apply_visitor(hlsl::printer{std::cout << "Parsed: "});
            std::cout << "
";
        }
        else
        {
            std::cout << "Parse failed at " << quoted(std::string(f, l)) << "
";
        }
    }
}

【讨论】:

    【解决方案2】:

    I also figured out how the semantic actions write to _val(ctx) across multiple sequence '>>' operators. You can write to them with the type that you need and it gets passed to the next one!

    See binary2 rule and how it's def uses two semantic actions to write a Binary2 ast node and set different members each time.

    #include <boost/fusion/adapted.hpp>
    #include <boost/spirit/home/x3.hpp>
    #include <boost/spirit/home/x3/support/ast/variant.hpp>
    #include <boost/spirit/home/x3/support/utility/error_reporting.hpp>
    #include <iomanip>
    #include <iostream>
    
    namespace x3 = boost::spirit::x3;
    
    namespace hlsl
    {
        namespace ast
        {
            struct Void
            {
            };
            struct Get;
            struct Set;
            struct Call;
            struct Assign;
            struct Divide;
            struct Multiply;
            struct Unary;
            struct Binary2;
    
            struct Variable
            {
                std::string name;
                // operator std::string() const {
                //     return name;
                // }
            };
    
            using Expr = x3::variant<Void, x3::forward_ast<Get>, x3::forward_ast<Set>, Variable, x3::forward_ast<Call>, x3::forward_ast<Assign>, x3::forward_ast<Multiply>,  x3::forward_ast<Binary2>, x3::forward_ast<Divide>, x3::forward_ast<Unary>>;
    
            struct Call
            {
                Expr name;
                std::vector<Expr> arguments_;
            };
    
            struct Get
            {
                Expr object_;
                std::string property_;
            };
    
            struct Set
            {
                Expr object_;
                Expr value_;
                std::string name_;
            };
            struct Assign
            {
                std::string name_;
                Expr value_;
            };
            // struct Logical
            // {
            //     Expr left_;
            //     std::string op_;
            //     Expr right_;
            // };
    
            struct Multiply
            {
                Expr left_;
                Expr right_;
            };
    
            struct Binary2
            {
                Expr left_;
                std::string op_;
                Expr right_;
            };
            struct Divide
            {
                Expr left_;
                Expr right_;
            };
    
            struct Unary
            {
                std::string op_;
                Expr expr_;
            };
        } // namespace ast
    
        struct printer
        {
            std::ostream &_os;
            using result_type = void;
    
            void operator()(hlsl::ast::Get const &get) const
            {
                _os << "get { object_:";
                get.object_.apply_visitor(*this);
                _os << ", property_:" << quoted(get.property_) << " }";
            }
    
            void operator()(hlsl::ast::Set const &set) const
            {
                _os << "set { object_:";
                set.object_.apply_visitor(*this);
                _os << ", name_:" << quoted(set.name_);
                _os << " equals: ";
                set.value_.apply_visitor(*this);
                _os << " }";
            }
    
            void operator()(hlsl::ast::Assign const &assign) const
            {
                _os << "assign { ";
                _os << "name_:" << quoted(assign.name_);
                _os << ", value_:";
                assign.value_.apply_visitor(*this);
                _os << " }";
            }
    
            void operator()(hlsl::ast::Variable const &var) const
            {
                _os << "var{" << quoted(var.name) << "}";
            };
            void operator()(hlsl::ast::Divide const &bin) const
            {
                _os << "divide { ";
                bin.left_.apply_visitor(*this);
                bin.right_.apply_visitor(*this);
                _os << " }";
            };
            void operator()(hlsl::ast::Multiply const &bin) const
            {
                _os << "multiply { ";
                bin.left_.apply_visitor(*this);
                bin.right_.apply_visitor(*this);
                _os << " }";
            };
    
            void operator()(hlsl::ast::Binary2 const &bin) const
            {
                _os << "binary2 { ";
                bin.left_.apply_visitor(*this);
                _os << bin.op_ << ", ";
                bin.right_.apply_visitor(*this);
                _os << " }";
            };
    
            void operator()(hlsl::ast::Unary const &un) const
            {
                _os << "unary { ";
                un.expr_.apply_visitor(*this);
                _os << quoted(un.op_);
                _os << " }";
            };
            void operator()(hlsl::ast::Call const &call) const
            {
                _os << "call{";
                call.name.apply_visitor(*this);
                _os << ", args: ";
    
                for (auto &arg : call.arguments_)
                {
                    arg.apply_visitor(*this);
                    _os << ", ";
                }
                _os << /*quoted(call.name) << */ "}";
            };
            void operator()(hlsl::ast::Void const &) const { _os << "void{}"; };
        };
    
    } // namespace hlsl
    
    BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Variable, name)
    BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Call, name, arguments_)
    BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Get, object_, property_)
    BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Set, object_, value_)
    BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Assign, name_, value_)
    BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Multiply, left_, right_)
    BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Binary2, left_, op_, right_)
    BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Divide, left_, right_)
    BOOST_FUSION_ADAPT_STRUCT(hlsl::ast::Unary, op_, expr_)
    
    namespace hlsl::parser
    {
        struct eh_tag;
    
        struct error_handler
        {
            template <typename It, typename Exc, typename Ctx>
            auto on_error(It &, It, Exc const &x, Ctx const &context) const
            {
                x3::get<eh_tag>(context)( //
                    x.where(), "Error! Expecting: " + x.which() + " here:");
    
                return x3::error_handler_result::fail;
            }
        };
    
        struct program_ : error_handler
        {
        };
    
        x3::rule<struct identifier_, std::string> const identifier{"identifier"};
        x3::rule<struct binop_, std::string> const binop{"binop"};
    
        x3::rule<struct variable_, ast::Variable> const variable{"variable"};
        x3::rule<struct arguments_, std::vector<ast::Expr>> const arguments{"arguments_"};
        x3::rule<struct binary_, hlsl::ast::Expr> const binary{"binary"};
        x3::rule<struct binary2_, hlsl::ast::Expr> const binary2{"binary2"};
    
        x3::rule<struct multiply_, hlsl::ast::Expr> const multiply{"multiply"};
        x3::rule<struct divide_, hlsl::ast::Expr> const divide{"divide"};
    
        x3::rule<struct unary_, hlsl::ast::Unary> const unary{"unary"};
        x3::rule<struct unarycallwrapper_, hlsl::ast::Expr> const unarycallwrapper{"unarycallwrapper"};
        x3::rule<struct get_, ast::Expr> const get{"get"};
        x3::rule<struct call_, ast::Expr> const call{"call"};
        x3::rule<struct program_, ast::Expr> const program{"program"};
        x3::rule<struct primary_, ast::Expr> const primary{"primary"};
        x3::rule<struct expression_, ast::Expr> const expression{"expression"};
        x3::rule<struct set_, ast::Set, true> const set{"set"};
        x3::rule<struct assign_, ast::Assign> const assign{"assign"};
        x3::rule<struct assignment_, ast::Expr> const assignment{"assignment"};
    
        auto get_string_from_variable = [](auto &ctx)
        { _val(ctx).name_ = std::move(_attr(ctx).name); };
    
        auto fix_assignExpr = [](auto &ctx)
        { _val(ctx).value_ = std::move(_attr(ctx)); };
    
        auto as_expr = [](auto &ctx)
        { _val(ctx) = ast::Expr(std::move(_attr(ctx))); };
    
        auto as_unary = [](auto &ctx)
        { _val(ctx) = ast::Unary(std::move(_attr(ctx))); };
    
        auto as_call = [](auto &ctx)
        { _val(ctx) = ast::Call{std::move(_val(ctx)), std::move(_attr(ctx))}; };
    
        auto as_multiply = [](auto &ctx)
        { _val(ctx) = ast::Multiply{std::move(_val(ctx)), std::move(_attr(ctx))}; };
    
        auto as_divide = [](auto &ctx)
        { _val(ctx) = ast::Divide{std::move(_val(ctx)), std::move(_attr(ctx))}; };
    
        auto as_binary2A = [](auto &ctx)
        { _val(ctx) = ast::Binary2{std::move(_val(ctx)), std::move(_attr(ctx)), ast::Expr{}}; };
    
        auto as_binary2B = [](auto &ctx)
        { //_val(ctx) = std::move(_val(ctx));
            boost::get<x3::forward_ast<ast::Binary2>>(_val(ctx)).get().right_ = std::move(_attr(ctx)); };
    
        auto fold_in_get_to_set = [](auto &ctx)
        {
            auto &val = x3::_val(ctx);
            val.name_ = boost::get<x3::forward_ast<ast::Get>>(val.object_).get().property_;
            val.object_ = ast::Expr(boost::get<x3::forward_ast<ast::Get>>(val.object_).get().object_);
        };
    
        auto as_string = [](auto &ctx)
        { _val(ctx) = std::move(_attr(ctx).name); };
        auto as_assign = [](auto &ctx)
        { _val(ctx) = ast::Assign(std::move(_val(ctx)), std::move(_attr(ctx))); };
        auto as_get = [](auto &ctx)
        {
            _val(ctx) = ast::Get{std::move(_val(ctx)), _attr(ctx)};
        };
    
        auto variable_def = identifier;
        auto primary_def = variable;
        auto identifier_def = x3::lexeme[x3::alpha >> *x3::alnum];
    
        auto expression_def = assignment;
        auto assignment_def = (assign | set) | binary2; // replace binary with call to see the rest working
        auto assign_def = variable[get_string_from_variable] >> '=' >> assignment[fix_assignExpr];
        auto set_def = (get >> '=' >> assignment)[fold_in_get_to_set];
    
        auto arguments_def = *(expression % ',');
        auto get_def = primary[as_expr] >> *('.' >> identifier)[as_get];
        auto call_def = primary[as_expr] >> *((x3::lit('(') >> arguments >> x3::lit(')'))[as_call] | ('.' >> identifier)[as_get]);
    
        auto unary_def = (x3::string("-") >> unarycallwrapper);
        auto unarycallwrapper_def =   unary | call;
        auto binop_def = x3::string("*") | x3::string("/");
        auto binary_def = unarycallwrapper[as_expr] >> *((x3::lit('/') >> unarycallwrapper[as_divide]) | (x3::lit('*') >> unarycallwrapper[as_multiply]));
        auto binary2_def = unarycallwrapper[as_expr] >> *(binop[as_binary2A] >> unarycallwrapper[as_binary2B]);
    
        auto program_def = x3::skip(x3::space)[expression];
    
        BOOST_SPIRIT_DEFINE(primary, assign, binop, binary, binary2, unary, unarycallwrapper, assignment, get, set, variable, arguments, expression, call, identifier, program);
    
    } // namespace hlsl::parser
    
    int main()
    {
        using namespace hlsl;
    
        for (std::string const input :
             {
                 "first",
                 "first.second",
                 "first.Second.third",
                 "first.Second().third",
                 "first.Second(arg1).third",
                 "first.Second(arg1, arg2).third",
                 "first = second",
                 "first.second = third",
                 "first.second.third = fourth",
                 "first.second.third = fourth()",
                 "first.second.third = fourth(arg1)",
                 "this * that",  // binary { var{"this"} "*" var{"that"} }
                 "this * -that", // binary { var{"this"} "*" unary{'-', var{"that"}} }
                 "this * that * there",
                 "this * that / there",
                 "this.inner * that * there.inner2",
             }) //
        {
            std::cout << "===== " << quoted(input) << "
    ";
            auto f = input.begin(), l = input.end();
    
            // Our error handler
            auto const p = x3::with<parser::eh_tag>(
                x3::error_handler{f, l, std::cerr})[hlsl::parser::program];
    
            if (hlsl::ast::Expr fs; parse(f, l, p, fs))
            {
                fs.apply_visitor(hlsl::printer{std::cout << "Parsed: "});
                std::cout << "
    ";
            }
            else
            {
                std::cout << "Parse failed at " << quoted(std::string(f, l)) << "
    ";
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-22
      • 2021-10-28
      • 2023-04-02
      相关资源
      最近更新 更多