簡體   English   中英

用Boost Spirit解析語法

[英]Parsing a grammar with Boost Spirit

我試圖像下面的樹表達式一樣解析C函數(使用Spirit Parser Framework ):

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)
)

至於調試,它可以使用正常的休息和觀看方法。 盡管如何格式化規則使這變得困難。 如果您根據精神示例進行格式化(每行〜一個解析器,每行一個鳳凰語句),斷點將提供更多信息。

你的數據結構沒有辦法區分A()SOME ,因為它們都是葉子(讓我知道我是否遺漏了什么)。 從您的變體注釋中,我認為這不是您的意圖,因此為了區分這兩種情況,我將一個bool commandFlag成員變量添加到MockExpressionNode(對於A()為true,對於SOME為false),以及相應的融合適配器行。

具體來說,您需要將開始規則傳遞給基礎構造函數,即:

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

這是語法的入口點,也是您沒有獲得任何數據解析的原因。 我很驚訝沒有它編譯,我認為語法類型需要匹配第一個規則的類型。 即便如此,這也是一個方便的慣例。

對於tag規則,實際上有兩個解析器qi::char_("a-zA-Z_") ,它是_1,類型為char*qi::char_("a-zA-Z_0-9")是_2與類型(基本上) vector<char> 不可能將這些強制轉換為沒有autorule的字符串,但可以通過將規則附加到每個已解析的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 ) ] 這將解析零或多個指令規則,后跟“,”。 它還嘗試push_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原始結構A()之間實際上沒有區別,你可以只使用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 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM