-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcoliru_sehe-debug-simple-compare.cpp
397 lines (381 loc) · 10.1 KB
/
coliru_sehe-debug-simple-compare.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
//OriginalSource:
// http://coliru.stacked-crooked.com/a/b4de81a919bec94a
//LinkedToFrom:
// https://stackoverflow.com/questions/74963861/parse-html-with-boost-spirit-x3
//Changes:
/*
Simplification:
Simplified parser expression of tag_name to just x3::int_.
The justification for this simplification is the essential purpose
for this file is to show how to handle testing for same begin end
tags. That purpose is not advanced using much more complex parser
expression in the original code:
auto tag_name_
= x3::rule<struct HtmlTagName_tag, std::string>{"HtmlTagName"}
= x3::lexeme[*~x3::char_(" />")];
Similar simplifications were made for other parser expressions.
Also, some renames were made. In particular Parser::inner_text
was renamed to Parser::html_content_ to conform to the naming
convention for Parser::tag_name_ and attribute Ast::tag_name. The
original code had no Ast::html_content, instead, it had, in
element_base, std::string. The following code is clearer in that
it using a descriptive name, Ast::html_content instead of
std::string, to name the attribute.
In addition, the primitive parser definitions were moved above the
'namespace Ast' to allow proper attributes, using
x3::traits::attribute_of, to be calculated for the primitive
Ast's.
Comparison:
An alternative method to using semantic actions is using attribute transforms
done by as_attr.
The alternative's are selected based on defined(USE_SEMANTIC_ACTIONS).
*/
//===============
#include <iomanip>
#include <iostream>
#include <string>
#define ISSUE749_EXE_TRACE "issue749/coliru_sehe-debug-simple-compare.cpp"
#ifdef ISSUE749_EXE_TRACE
#include <boost/iostreams/utility/templ_expr/demangle_fmt_type.hpp>
#include <boost/utility/trace_scope.hpp>
using boost::trace_scope;
#else
#include "trace_scope_dummy.hpp"
#endif//ISSUE749_EXE_TRACE
//#define BOOST_SPIRIT_X3_DEBUG
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <iomanip>
#include <iostream>
//#define USE_SEMANTIC_ACTIONS
#ifdef USE_SEMANTIC_ACTIONS
#include <stack>
#else
#include <boost/spirit/home/x3/core/make_transform_parser.hpp>
#include <boost/fusion/sequence/intrinsic/at_c.hpp>
#endif//USE_SEMANTIC_ACTIONS
namespace x3 = boost::spirit::x3;
using namespace std::string_literals;
namespace Parser
//primitive parsers
{
auto html_content_ =
x3::alpha
;
auto tag_name_ =
x3::uint_
;
}//Parser
namespace Ast
{
struct html_tag;
using html_content =
typename x3::traits::
attribute_of
< std::remove_const_t<decltype(Parser::html_content_)>
, x3::unused_type
>::type
;
using html_element =
x3::variant
< html_content
, boost::recursive_wrapper<html_tag>
>;
using html_elements =
std::vector<html_element>
;
using tag_name =
typename x3::traits::
attribute_of
< std::remove_const_t<decltype(Parser::tag_name_)>
, x3::unused_type
>::type
;
struct html_tag
{
tag_name header;
html_elements children;
};
} // namespace Ast
BOOST_FUSION_ADAPT_STRUCT(Ast::html_tag, header, children)
namespace Parser
{
struct html_element__tag;
#ifdef USE_SEMANTIC_ACTIONS
struct
tag_stack final
{};
//#define TAG_NAME_FUNC_TRACE
auto tag_name_begin_func = [](auto& ctx)
{
#ifdef TAG_NAME_FUNC_TRACE
trace_scope ts("tag_name_begin_func");
#endif
auto& tag_stk = get<tag_stack>(ctx);
Ast::tag_name tag_beg=_attr(ctx);
#ifdef TAG_NAME_FUNC_TRACE
std::cout<<":tag_beg="<<tag_beg<<";\n";
#endif
tag_stk.push(tag_beg);
};
auto tag_name_end_func = [](auto& ctx)
{
#ifdef TAG_NAME_FUNC_TRACE
trace_scope ts("tag_name_end_func");
#endif
auto& tag_stk = get<tag_stack>(ctx);
Ast::tag_name tag_beg = tag_stk.top();
Ast::tag_name tag_end = _attr(ctx);
auto tags_matched=tag_beg == tag_end;
#ifdef TAG_NAME_FUNC_TRACE
std::cout<<":tag_end="<<tag_end<<";\n";
std::cout<<":tags_matched="<<tags_matched<<";\n";
#endif
_pass(ctx) = tags_matched;
tag_stk.pop();
};
#endif//USE_SEMANTIC_ACTIONS
auto
tag_header_
=
( '<'
>> tag_name_
>> '>'
)
#ifdef USE_SEMANTIC_ACTIONS
[ tag_name_begin_func
]
#endif//USE_SEMANTIC_ACTIONS
;
auto
tag_footer_
=
( "</"
#ifdef USE_SEMANTIC_ACTIONS
>> x3::omit
[ tag_name_
[ tag_name_end_func
]
]
#else
>> tag_name_
#endif//USE_SEMANTIC_ACTIONS
>> '>'
)
;
x3::rule<html_element__tag, Ast::html_element>
html_element_ = "HtmlElement"
;
auto
tag_block__def=
tag_header_
>> *html_element_
>> tag_footer_
;
#ifndef USE_SEMANTIC_ACTIONS
//#define USE_AS_ATTR_TAG
#ifdef USE_AS_ATTR_TAG
struct tag_block__tag
//This tag name was cp'ed from the OriginalSource where it was the
//rule_id for the tag_block_ rule.
//
//Such a rule is unneeded, but the tag name is useful for
//defining a specific as_attr below.
//
//Otherwise, it's completely arbitrary as long as no other
//as_attr with same otherwise args is used.
//
//Actually, in case there are no otherwise args used,
//this could be eliminated and the default tag used.
//This can be seen by undefining USE_AS_ATTR_TAG
//and seeing the same results.
;
#endif
#endif//USE_SEMANTIC_ACTIONS
auto
tag_block_=
#ifdef USE_SEMANTIC_ACTIONS
tag_block__def
//transform attribute_of tag_block__def to Ast::html_tag
//using above semantic actions.
#else
x3::traits::as_attr
< Ast::html_tag
#ifdef USE_AS_ATTR_TAG
, x3::traits::transform_tag_id<tag_block__tag>
#endif
>
[ tag_block__def
]
//transform attribute_of tag_block__def to Ast::html_tag
//using make_transform_attribute.hpp as_attr.
#endif//USE_SEMANTIC_ACTIONS
;
auto html_element__def =
html_content_
| tag_block_
;
auto start =
#ifdef USE_SEMANTIC_ACTIONS
x3::with<tag_stack>
( std::stack<Ast::tag_name>{}
)
[ html_element_
]
#else
html_element_
#endif//USE_SEMANTIC_ACTIONS
;
BOOST_SPIRIT_DEFINE(html_element_)
}
#ifndef USE_SEMANTIC_ACTIONS
namespace boost
{
namespace spirit
{
namespace x3
{
namespace traits
{
//#define TRANSFORM_ATTRIBUTE_TAGGED_TAG_BLOCK__TAG
template
< typename ToFrom
//attribute_of<decltype(Parser::tag_block__def),Context>::type
//IOW, the attribute_of the parser passed as operator[] arg
//to the as_attr call in Parser namespace.
>
struct
transform_attribute_tagged
< Ast::html_tag
, ToFrom
#ifdef USE_AS_ATTR_TAG
, x3::traits::transform_tag_id<Parser::tag_block__tag>
#endif
>
{
using FromTo=Ast::html_tag;
using type=ToFrom;
static type
pre
( FromTo&
)
{
#ifdef TRANSFORM_ATTRIBUTE_TAGGED_TAG_BLOCK__TAG
boost::trace_scope
ts
( stringify
( ISSUE749_EXE_TRACE
, ':',__LINE__
, ':',__func__
)
);
std::cout<<":type=\n"<<demangle_fmt_type<type>()<<";\n";
#endif
return ToFrom();
}
static bool
post
( FromTo& val
, ToFrom const& attribute
)
{
#ifdef TRANSFORM_ATTRIBUTE_TAGGED_TAG_BLOCK__TAG
boost::trace_scope
ts
( stringify
( ISSUE749_EXE_TRACE
, ':',__LINE__
, ':',__func__
)
);
#endif
using boost::fusion::at_c;
Ast::tag_name tag_beg=at_c<0>(attribute);//attribute_of tag_header_
Ast::tag_name tag_end=at_c<2>(attribute);//attribute_of tag_footer_
bool const matched=tag_beg == tag_end;
#ifdef TRANSFORM_ATTRIBUTE_TAGGED_TAG_BLOCK__TAG
std::cout
<<":tag_beg="<<tag_beg<<"\n"
<<":tag_end="<<tag_end<<";\n"
;
#endif
if(matched)
{
at_c<0>(val)=at_c<0>(attribute);
at_c<1>(val)=at_c<1>(attribute);//attribute_of *html_element_
}
return matched;
}
};
}//traits
}//x3
}//spirit
}//boost
#endif//ndef USE_SEMANTIC_ACTIONS
#include "unit_tests.hpp"
int main() {
#if 1 && defined(ISSUE749_EXE_TRACE)
boost::iostreams::indent_scoped_ostreambuf<char>
indent_outbuf(std::cout,2);
#endif
std::cout<<std::boolalpha;
#if 0
{
trace_scope ts("tag_name_");
unit_tests::test
( Parser::tag_name_
, { R"(123)"
, R"(456)"
}
);
}
#endif
#if 0
{
trace_scope ts("html_content_ pass");
unit_tests::test
( Parser::html_content_
, { R"(a)"
, R"(b)"
}
);
}
#endif
#if 0
{
trace_scope ts("html_content_ fail");
unit_tests::test<false>
( Parser::html_content_
, { R"(1)"
, R"(23)"
}
);
}
#endif
#if 1
{
trace_scope ts("start pass");
unit_tests::test
( Parser::start
, { R"(a)"
, R"(<0></0>)"
, R"(<0>a</0>)"
, R"(<0>a b c</0>)"
, R"(<0>a b<1>c d</1> e</0>)"
}
);
}
#endif
#if 1
{
trace_scope ts("start fail");
unit_tests::test<false>
( Parser::start
, { R"(<0></1>)"
, R"(<0>a b c</1>)"
}
);
}
#endif
}