سؤال

أحاول تحليل وظيفة C مثل تعبيرات الأشجار مثل ما يلي (باستخدام إطار محلل الروح):

F( A() , B( GREAT( SOME , NOT ) ) , C( YES ) )

لهذا أحاول استخدام القواعد الثلاث على القواعد التالية:

template< typename Iterator , typename ExpressionAST >
struct InputGrammar : qi::grammar<Iterator, ExpressionAST(), space_type> {

    InputGrammar() : InputGrammar::base_type( ) {
       tag = ( qi::char_("a-zA-Z_")  >> *qi::char_("a-zA-Z_0-9") )[ push_back( at_c<0>(qi::_val) , qi::_1 ) ];
       command =  tag [ at_c<0>(qi::_val) = at_c<0>(qi::_1) ] >> "(" >> (*instruction >> ",")
                                        [ push_back( at_c<1>(qi::_val) , qi::_1 ) ]  >> ")";
       instruction = ( command | tag ) [qi::_val = qi::_1];
    }
    qi::rule< Iterator , ExpressionAST() , space_type > tag;
    qi::rule< Iterator , ExpressionAST() , space_type > command;
    qi::rule< Iterator , ExpressionAST() , space_type > instruction;
};

لاحظ أن قاعدة العلامة الخاصة بي تحاول فقط التقاط المعرفات المستخدمة في التعبيرات (أسماء "الوظيفة"). لاحظ أيضًا أن توقيع قاعدة العلامة يعيد أ ExpressionAST بدل من std::string, ، كما هو الحال في معظم الأمثلة. السبب في أنني أريد أن أفعل ذلك مثل هذا هو في الواقع بسيط للغاية: أكره استخدام المتغيرات وسأتجنها إذا أمكن. سيكون من الرائع الحفاظ على الكعكة وتناولها أيضًا.

يجب أن يبدأ الأمر بعلامة (اسم العقدة الحالية ، وحقل السلسلة الأولى من عقدة AST) وعدد متغير من الوسائط المحاطة بالأقواس ، ويمكن أن يكون كل من الوسائط علامة أو أمر آخر.

ومع ذلك ، هذا المثال لا يعمل على الإطلاق. يجمع وكل شيء ، ولكن في وقت التشغيل يفشل في تحليل جميع سلاسل الاختبار الخاصة بي. والشيء الذي يزعجني حقًا هو أنه لا يمكنني تحديد كيفية إصلاحه ، حيث لا يمكنني فعل تصحيح الكود أعلاه ، على الأقل بالمعنى التقليدي للكلمة. في الأساس ، الطريقة الوحيدة التي أراها يمكنني إصلاح الرمز أعلاه هي معرفة ما أفعله خطأ.

لذا ، فإن السؤال هو أنني لا أعرف ما هو الخطأ في الكود أعلاه. كيف يمكنك تحديد القواعد المذكورة أعلاه؟

ال ExpressionAST الكتابة الذي أستخدمه هو:

struct MockExpressionNode {
    std::string name;
    std::vector< MockExpressionNode > operands;

    typedef std::vector< MockExpressionNode >::iterator iterator;
    typedef std::vector< MockExpressionNode >::const_iterator const_iterator;

    iterator begin() { return operands.begin(); }
    const_iterator begin() const { return operands.begin(); }
    iterator end() { return operands.end(); }
    const_iterator end() const { return operands.end(); }

    bool is_leaf() const {
        return ( operands.begin() == operands.end() );
    }
};

BOOST_FUSION_ADAPT_STRUCT(
    MockExpressionNode,
    (std::string, name)
    (std::vector<MockExpressionNode>, operands)
)
هل كانت مفيدة؟

المحلول

بقدر تصحيح الأخطاء ، من الممكن استخدام نهج استراحة ومراقبة طبيعية. هذا يصعب على كيفية تنسيق القواعد رغم ذلك. إذا قمت بتنسيق أمثلة الروح (~ محلل واحد لكل سطر ، بيان Phoenix واحد لكل سطر) ، ستكون نقاط الاستراحة أكثر إفادة بكثير.

بنية البيانات الخاصة بك ليس لديها وسيلة للتمييز A() من SOME في ذلك كلاهما أوراق (اسمحوا لي أن أعرف إذا كنت أفتقد شيئًا). من تعليقك المتغير ، لا أعتقد أن هذا كان نيتك ، لذا في تمييز هاتين الحالتين ، أضفت أ bool commandFlag متغير عضو في mockexpressionnode (صحيح ل A() وكاذبة ل SOME) ، مع خط محول الانصهار المقابل.

بالنسبة للرمز على وجه التحديد ، تحتاج إلى تمرير قاعدة البدء إلى مُنشئ الأساس ، أي:

InputGrammar() : InputGrammar::base_type(instruction) {...}

هذه هي نقطة الدخول في القواعد ، ولهذا السبب لم تحصل على أي بيانات. لقد فوجئت بتجميعها بدونها ، اعتقدت أن نوع القواعد كان مطلوبًا لمطابقة نوع القاعدة الأولى. ومع ذلك ، فهذه اتفاقية مريحة لمتابعة.

ل tag القاعدة ، هناك بالفعل اثنين من المحللين qi::char_("a-zA-Z_"), ، وهو _1 مع النوع char و *qi::char_("a-zA-Z_0-9") وهو _2 مع النوع (أساسا) vector<char>. لا يمكن إجبارها على سلسلة بدون آلي ، ولكن يمكن القيام بها عن طريق إرفاق قاعدة لكل شار محفوظ:

tag =   qi::char_("a-zA-Z_")
        [ at_c<0>(qi::_val) = qi::_1 ];
    >> *qi::char_("a-zA-Z_0-9")           //[] has precedence over *, so _1 is 
        [ at_c<0>(qi::_val) += qi::_1 ];  //  a char rather than a vector<char>

ومع ذلك ، فإنه أنظف بكثير للسماح للروح القيام بهذا التحويل. لذا حدد قاعدة جديدة:

qi::rule< Iterator , std::string(void) , ascii::space_type > identifier;
identifier %= qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9");

ولا تقلق بشأنه ؛). ثم تصبح العلامة

tag = identifier
      [
          at_c<0>(qi::_val) = qi::_1,
          ph::at_c<2>(qi::_val) = false //commandFlag
      ]

بالنسبة إلى القيادة ، الجزء الأول على ما يرام ، ولكن هناك مشاكل مع زوجين (*instruction >> ",")[ push_back( at_c<1>(qi::_val) , qi::_1 ) ]. سيؤدي ذلك إلى تحليل صفر أو قواعد تعليمية متعددة تليها "،". كما يحاول دفع _back vector<MockExpressionNode> (لست متأكدًا من سبب تجميع هذا أيضًا ، ربما لم يتم إنشاء مثيل له بسبب قاعدة البدء المفقودة؟). أعتقد أنك تريد ما يلي (مع تعديل المعرف):

command =
        identifier
        [
           ph::at_c<0>(qi::_val) = qi::_1, 
           ph::at_c<2>(qi::_val) = true    //commandFlag
        ]
    >>  "("
    >> -(instruction % ",")
        [
           ph::at_c<1>(qi::_val) = qi::_1
        ]
    >>  ")";

هذا يستخدم المشغل الاختياري - ومشغل القائمة %, ، هذا الأخير يعادل instruction >> *("," >> instruction). ثم يقوم تعبير Phoenix بتعيين المتجه مباشرةً إلى عضو الهيكل ، ولكن يمكنك أيضًا إرفاق الإجراء مباشرة بمطابقة التعليمات واستخدام Push_back.

قاعدة التعليمات على ما يرام ، سأذكر فقط أنه يعادل instruction %= (command|tag).

شيء أخير ، إذا لم يكن هناك تمييز بين A() و SOME (أي هيكلك الأصلي بدون commandFlag) ، يمكنك كتابة هذا المحلل باستخدام Autorules فقط:

template< typename Iterator , typename ExpressionAST >
struct InputGrammar : qi::grammar<Iterator, ExpressionAST(), ascii::space_type> {
   InputGrammar() : InputGrammar::base_type( command ) {
      identifier %=
             qi::char_("a-zA-Z_")
         >> *qi::char_("a-zA-Z_0-9");
      command %=
            identifier
         >> -(
            "("
         >> -(command % ",")
         >>  ")");
    }
    qi::rule< Iterator , std::string(void) , ascii::space_type > identifier;
    qi::rule< Iterator , ExpressionAST(void) , ascii::space_type > command;
};

هذه هي الفائدة الكبيرة لاستخدام بنية ملفوفة بالاندماج التي تقوم بتصوير الإدخال عن كثب.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top