2010-06-18 14 views
50

trong article about boost spirit semantic actions này nó được đề cập rằngtinh thần thúc đẩy thông số hành động ngữ nghĩa

Thực tế, có hơn 2 đối số được thông qua: bối cảnh phân tích cú pháp và một tham chiếu đến một boolean ‘nhấn’ tham số. Ngữ cảnh phân tích cú pháp là chỉ có ý nghĩa nếu hành động ngữ nghĩa được gắn ở đâu đó bên phải của quy tắc bên phải của quy tắc. Chúng tôi sẽ sớm xem thêm thông tin về số này . Giá trị boolean có thể được đặt thành false bên trong hành động ngữ nghĩa vô hiệu hóa kết quả trong quá trình hồi tưởng, làm cho trình phân tích cú pháp không thành công.

Tất cả đều ổn, nhưng tôi đã cố gắng tìm một ví dụ chuyển đối tượng hàm làm tác vụ ngữ nghĩa sử dụng các tham số khác (ngữ cảnh phân tích cú pháp và nhấn boolean) nhưng tôi chưa tìm thấy bất kỳ. Tôi rất thích xem một ví dụ bằng cách sử dụng các hàm hoặc hàm chức năng thông thường, vì tôi hầu như không thể mò mẫm phượng hoàng phượng hoàng

Trả lời

62

Đây là một câu hỏi thực sự hay (vì nó có thể là sâu) vì nó ở giao diện của qi và phượng hoàng. Tôi cũng chưa thấy một ví dụ nào, vì vậy tôi sẽ mở rộng bài viết một chút theo hướng này.

Như bạn nói, chức năng cho semantic actions có thể mất đến ba thông số

  1. thuộc tính khớp - được đề cập trong bài viết
  2. Context - chứa các giao diện qi-phượng
  3. trận đấu cờ - thao tác trận đấu nhà nước

trận đấu cờ

Như bài viết nói, tham số thứ hai không có ý nghĩa trừ khi biểu thức là một phần của quy tắc, vì vậy hãy bắt đầu với thứ ba. Tuy nhiên, trình giữ chỗ cho tham số thứ hai vẫn là cần thiết và cho việc sử dụng này boost::fusion::unused_type. Vì vậy, một chức năng sửa đổi từ bài viết để sử dụng tham số thứ ba là:

#include <boost/spirit/include/qi.hpp> 
#include <string> 
#include <iostream> 

void f(int attribute, const boost::fusion::unused_type& it, bool& mFlag){ 
    //output parameters 
    std::cout << "matched integer: '" << attribute << "'" << std::endl 
       << "match flag: " << mFlag << std::endl; 

    //fiddle with match flag 
    mFlag = false; 
} 

namespace qi = boost::spirit::qi; 

int main(void){ 
    std::string input("1234 6543"); 
    std::string::const_iterator begin = input.begin(), end = input.end(); 

    bool returnVal = qi::phrase_parse(begin, end, qi::int_[f], qi::space); 

    std::cout << "return: " << returnVal << std::endl; 
    return 0; 
} 

mà kết quả đầu ra:

 
matched integer: '1234' 
match flag: 1 
return: 0 

Tất cả các ví dụ này, không có gì chuyển trận đấu để một tổ chức phi so khớp, được phản ánh trong đầu ra của trình phân tích cú pháp. Theo hkaiser, trong tăng 1.44 và lên thiết lập cờ trận đấu thành false sẽ khiến trận đấu thất bại theo cách thông thường. Nếu các lựa chọn thay thế được xác định, trình phân tích cú pháp sẽ quay trở lại và cố gắng khớp chúng như mong đợi. Tuy nhiên, trong việc tăng cường < = 1.43 một lỗi Spirit ngăn chặn backtracking, gây ra hành vi lạ.Để thấy điều này, thêm phượng bao gồm boost/spirit/include/phoenix.hpp và thay đổi biểu thức để

qi::int_[f] | qi::digit[std::cout << qi::_1 << "\n"] 

Bạn mong muốn rằng, khi khí :: int phân tích cú pháp không thành công, thay thế khí :: chữ số để phù hợp với đầu của đầu vào tại " 1" , nhưng sản lượng là:

 
matched integer: '1234' 
match flag: 1 
6 
return: 1 

các 6 là chữ số đầu tiên của int thứ hai trong các đầu vào mà chỉ thay thế được thực hiện bằng cách sử dụng đội trưởng và không thụt lùi. Cũng lưu ý rằng trận đấu được coi là thành công, dựa trên giải pháp thay thế.

Khi tăng 1.44, cờ kết hợp sẽ hữu ích khi áp dụng tiêu chí phù hợp có thể khó diễn tả trong trình tự phân tích cú pháp. Lưu ý rằng cờ phù hợp có thể được điều khiển bằng các biểu thức phượng bằng cách sử dụng trình giữ chỗ _pass.

tham số Context

Tham số thú vị hơn là cái thứ hai, trong đó có giao diện qi-phượng, hoặc trong khí cách nói, bối cảnh của hành động ngữ nghĩa. Để minh họa điều này, đầu tiên kiểm tra một quy tắc:

rule<Iterator, Attribute(Arg1,Arg2,...), qi::locals<Loc1,Loc2,...>, Skipper> 

Tham số bối cảnh là hiện thân của những thuộc tính, arg1, ... ArgN, và khí :: dân địa phương mẫu paramters, được bọc trong một tăng :: mẫu tinh thần :: bối cảnh loại . Thuộc tính này khác với tham số hàm: thuộc tính tham số hàm là giá trị được phân tích cú pháp, trong khi thuộc tính này là giá trị của chính quy tắc. Một hành động ngữ nghĩa phải ánh xạ từ trước tới hành động thứ hai. Dưới đây là một ví dụ về một loại bối cảnh có thể (biểu phượng tương đương chỉ):

using namespace boost; 
spirit::context<    //context template 
    fusion::cons<    
     int&,     //return int attribute (phoenix: _val) 
     fusion::cons< 
      char&,   //char argument1  (phoenix: _r1) 
      fusion::cons< 
       float&,  //float argument2  (phoenix: _r2) 
       fusion::nil //end of cons list 
      >, 
     >, 
    >, 
    fusion::vector2<   //locals container 
     char,     //char local   (phoenix: _a) 
     unsigned int   //unsigned int local (phoenix: _b) 
    > 
> 

Note thuộc tính lợi nhuận và danh sách đối số mang hình thức của một danh sách lisp kiểu (một cons list). Để truy cập các biến này trong một hàm, hãy truy cập vào các thành viên attribute hoặc locals của mẫu cấu trúc context với sự hợp nhất :: tại <>(). Ví dụ, đối với một bối cảnh biến con

//assign return attribute 
fusion::at_c<0>(con.attributes) = 1; 

//get the second rule argument 
float arg2 = fusion::at_c<2>(con.attributes); 

//assign the first local 
fusion::at_c<1>(con.locals) = 42; 

Để sửa đổi các ví dụ bài viết để sử dụng đối số thứ hai, thay đổi chức năng định nghĩa và phrase_parse cuộc gọi:

... 
typedef 
    boost::spirit::context< 
     boost::fusion::cons<int&, boost::fusion::nil>, 
     boost::fusion::vector0<> 
    > f_context; 
void f(int attribute, const f_context& con, bool& mFlag){ 
    std::cout << "matched integer: '" << attribute << "'" << std::endl 
      << "match flag: " << mFlag << std::endl; 

    //assign output attribute from parsed value  
    boost::fusion::at_c<0>(con.attributes) = attribute; 
} 
... 
int matchedInt; 
qi::rule<std::string::const_iterator,int(void),ascii::space_type> 
    intRule = qi::int_[f]; 
qi::phrase_parse(begin, end, intRule, ascii::space, matchedInt); 
std::cout << "matched: " << matchedInt << std::endl; 
.... 

Đây là một ví dụ rất đơn giản rằng chỉ cần bản đồ giá trị được phân tích cú pháp đến giá trị thuộc tính đầu ra, nhưng các phần mở rộng phải khá rõ ràng. Chỉ cần làm cho bối cảnh struct mẫu tham số phù hợp với đầu ra quy tắc, đầu vào, và các loại địa phương. Lưu ý rằng loại hình này của một trận đấu trực tiếp giữa loại/giá trị phân tích cú pháp để loại sản lượng/giá trị có thể được thực hiện tự động sử dụng quy tắc tự động, với một %= thay vì một = khi xác định quy tắc:

qi::rule<std::string::const_iterator,int(void),ascii::space_type> 
    intRule %= qi::int_; 

IMHO, viết một hàm cho mỗi hành động sẽ khá tẻ nhạt, so với các biểu thức tương đương ngắn và dễ đọc. Tôi thông cảm với quan điểm voodoo, nhưng một khi bạn làm việc với phượng hoàng trong một thời gian ngắn, ngữ nghĩa và cú pháp không quá khó khăn.

Edit: Truy cập vào bối cảnh quy tắc w/Phoenix

Biến bối cảnh chỉ được xác định khi phân tích cú pháp là một phần của một quy tắc. Hãy nghĩ về một trình phân tích cú pháp như là bất kỳ biểu thức nào tiêu thụ đầu vào, trong đó quy tắc dịch các giá trị phân tích cú pháp (qi :: _ 1) thành một giá trị quy tắc (qi :: _ val). Sự khác biệt thường không nhỏ, ví dụ khi qi :: val có loại Lớp cần được xây dựng từ các giá trị được phân tích cú pháp POD. Dưới đây là một ví dụ đơn giản. Giả sử một phần đầu vào của chúng tôi là một chuỗi gồm ba số nguyên CSV (x1, x2, x3) và chúng tôi chỉ quan tâm đến hàm số học của ba số nguyên này (f = x0 + (x1 + x2) * x3), trong đó x0 là một giá trị thu được ở nơi khác. Một lựa chọn là đọc các số nguyên và tính toán hàm, hoặc cách khác là dùng phượng hoàng để làm cả hai.

Ví dụ này, sử dụng một quy tắc với thuộc tính đầu ra (giá trị hàm) và đầu vào (x0) và địa phương (để chuyển thông tin giữa các trình phân tích cú pháp riêng lẻ với quy tắc). Đây là ví dụ đầy đủ.

#include <boost/spirit/include/qi.hpp> 
#include <boost/spirit/include/phoenix.hpp> 
#include <string> 
#include <iostream> 

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

int main(void){ 
    std::string input("1234, 6543, 42"); 
    std::string::const_iterator begin = input.begin(), end = input.end(); 

    qi::rule< 
     std::string::const_iterator, 
     int(int),     //output (_val) and input (_r1) 
     qi::locals<int>,    //local int (_a) 
     ascii::space_type 
    > 
     intRule = 
      qi::int_[qi::_a = qi::_1]    //local = x1 
     >> "," 
     >> qi::int_[qi::_a += qi::_1]   //local = x1 + x2 
     >> "," 
     >> qi::int_ 
      [ 
       qi::_val = qi::_a*qi::_1 + qi::_r1 //output = local*x3 + x0 
      ]; 

    int ruleValue, x0 = 10; 
    qi::phrase_parse(begin, end, intRule(x0), ascii::space, ruleValue); 
    std::cout << "rule value: " << ruleValue << std::endl; 
    return 0; 
} 

Ngoài ra, tất cả các ints có thể được phân tích như một vector, và các chức năng đánh giá với một hành động ngữ nghĩa đơn (các % dưới đây là các nhà điều hành danh sách và các yếu tố của vectơ được truy cập với phượng :: at):

namespace ph = boost::phoenix; 
... 
    qi::rule< 
     std::string::const_iterator, 
     int(int), 
     ascii::space_type 
    > 
    intRule = 
     (qi::int_ % ",") 
     [ 
      qi::_val = (ph::at(qi::_1,0) + ph::at(qi::_1,1)) 
         * ph::at(qi::_1,2) + qi::_r1 
     ]; 
.... 

Đối với ở trên, nếu đầu vào là không chính xác (hai ints thay vì ba), điều tồi tệ có thể xảy ra vào thời gian chạy, vì vậy nó sẽ là tốt hơn để xác định số lượng các giá trị phân tích một cách rõ ràng, vì vậy phân tích cú pháp sẽ thất bại cho một đầu vào xấu. Dưới đây sử dụng _1, _2, và _3 để tham khảo đầu tiên, thứ hai, thứ ba và giá trị phù hợp:

(qi::int_ >> "," >> qi::int_ >> "," >> qi::int_) 
[ 
    qi::_val = (qi::_1 + qi::_2) * qi::_3 + qi::_r1 
]; 

Đây là một ví dụ giả tạo, nhưng sẽ cho bạn ý tưởng. Tôi đã tìm thấy các hành động ngữ nghĩa phượng thực sự hữu ích trong việc xây dựng các đối tượng phức tạp trực tiếp từ đầu vào; điều này là có thể bởi vì bạn có thể gọi các hàm tạo và các hàm thành viên trong các hành động ngữ nghĩa.

+3

Cảm ơn lời giải thích tuyệt vời đó. Bạn có nhớ tôi 'ăn cắp' điều này để repost nó trên trang web Spirit (chắc chắn cho tín dụng do)? Các vấn đề backtracking bạn đang đề cập đến là một lỗi trong Spirit. Hành vi đúng sau khi không thay thế đầu tiên nên là lựa chọn thứ hai bắt đầu lại tại cùng một điểm trong đầu vào như là phương án thay thế đầu tiên. Tôi sẽ xem những gì tôi có thể làm để sửa lỗi này. Hơn nữa, bạn không nên sử dụng trình giữ chỗ phượng hoàng trong các hành động ngữ nghĩa. Vui lòng luôn sử dụng trình giữ chỗ tương ứng của Thần, tức là qi :: _ 1. – hkaiser

+1

Ok, sự cố phát hành lại được khắc phục ngay bây giờ và sẽ ổn trong bản phát hành tiếp theo (Boost V1.44). – hkaiser

+1

@hkaiser Vui vì bạn thích nó và vui lòng sử dụng lại nếu bạn muốn. Tôi sẽ hỏi về vấn đề backtracking đó trong danh sách gửi thư, cảm ơn vì đã chăm sóc nó. Câu hỏi dành cho bạn trên trình giữ chỗ: cả hai 'phoenix :: _ 1' và 'qi :: _ 1' được định nghĩa là' const phoenix :: actor > ', chủ đề này có thay đổi không? – academicRobot