Đâ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ố
- thuộc tính khớp - được đề cập trong bài viết
- Context - chứa các giao diện qi-phượng
- 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.
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
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
@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