Skip to content

Commit 53b61f0

Browse files
rkaminskfoonathan
authored andcommitted
Construct operator tags with state
Fixes #170.
1 parent bb36367 commit 53b61f0

File tree

6 files changed

+148
-2
lines changed

6 files changed

+148
-2
lines changed

CHANGELOG.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* Add `lexy::buffer::release()` and `lexy::buffer::adopt()`.
1010
* Add default argument to `lexy::dsl::flag()`.
1111
* Add `lexy::callback_with_state`.
12+
* Pass the parse state to the tag of `lexy::dsl::op` if required (#172).
1213
* Add missing `constexpr` to container callbacks and `lexy::as_string`.
1314
* Fix infinite loop in `dsl::delimited` when dealing with invalid code points (#173).
1415
* Fix swallowed errors from case-folding rules (#149).

docs/content/reference/dsl/operator.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ Values::
4747
* For overload 1, a unique type computed from the rule `r`.
4848
* For overload 2 and `Tag != void`, an object of the specified `Tag` type.
4949
If `Tag` is constructible from the iterator type of the input, it constructs it giving it the start position of the operator.
50+
Similarly, it is also possible to construct the `Tag` from the parse state and the iterator.
5051
Otherwise, it uses the default constructor.
5152
* For overload 2 and `Tag == void`, no tag value is produced.
5253
* For overload 3, an object of implementation-defined type that is implicitly convertible to the type of `Tag`, returning that value.

include/lexy/dsl/operator.hpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ namespace lexyd
9696
template <typename Tag, typename Reader>
9797
using _detect_op_tag_ctor = decltype(Tag(LEXY_DECLVAL(Reader).position()));
9898

99+
template <typename Tag, typename Reader, typename Context>
100+
using _detect_op_tag_ctor_with_state
101+
= decltype(Tag(*LEXY_DECLVAL(Context).control_block->parse_state,
102+
LEXY_DECLVAL(Reader).position()));
103+
99104
template <typename TagType, typename Literal, typename... R>
100105
struct _op : branch_base
101106
{
@@ -113,6 +118,10 @@ struct _op : branch_base
113118
= lexy::whitespace_parser<Context, lexy::parser_for<_seq_impl<R...>, NextParser>>;
114119
if constexpr (std::is_void_v<TagType>)
115120
return continuation::parse(context, reader, LEXY_FWD(args)...);
121+
else if constexpr (lexy::_detail::is_detected<_detect_op_tag_ctor_with_state, op_tag_type,
122+
Reader, Context>)
123+
return continuation::parse(context, reader, LEXY_FWD(args)...,
124+
op_tag_type(*context.control_block->parse_state, op.pos));
116125
else if constexpr (lexy::_detail::is_detected<_detect_op_tag_ctor, op_tag_type, Reader>)
117126
return continuation::parse(context, reader, LEXY_FWD(args)..., op_tag_type(op.pos));
118127
else
@@ -143,6 +152,12 @@ struct _op : branch_base
143152

144153
if constexpr (std::is_void_v<TagType>)
145154
return impl.template finish<continuation>(context, reader, LEXY_FWD(args)...);
155+
else if constexpr (lexy::_detail::is_detected<_detect_op_tag_ctor_with_state,
156+
op_tag_type, Reader, Context>)
157+
return impl
158+
.template finish<continuation>(context, reader, LEXY_FWD(args)...,
159+
op_tag_type(*context.control_block->parse_state,
160+
reader.position()));
146161
else if constexpr (lexy::_detail::is_detected<_detect_op_tag_ctor, op_tag_type, Reader>)
147162
return impl.template finish<continuation>(context, reader, LEXY_FWD(args)...,
148163
op_tag_type(reader.position()));
@@ -164,6 +179,10 @@ struct _op : branch_base
164179
= lexy::parser_for<Literal, lexy::parser_for<_seq_impl<R...>, NextParser>>;
165180
if constexpr (std::is_void_v<TagType>)
166181
return continuation::parse(context, reader, LEXY_FWD(args)...);
182+
else if constexpr (lexy::_detail::is_detected<_detect_op_tag_ctor_with_state,
183+
op_tag_type, Reader, Context>)
184+
return continuation::parse(context, reader, LEXY_FWD(args)...,
185+
op_tag_type(*context.control_block->parse_state, pos));
167186
else if constexpr (lexy::_detail::is_detected<_detect_op_tag_ctor, op_tag_type, Reader>)
168187
return continuation::parse(context, reader, LEXY_FWD(args)..., op_tag_type(pos));
169188
else
@@ -310,4 +329,3 @@ constexpr auto operator/(_opc<O1...>, _opc<O2...>)
310329
} // namespace lexyd
311330

312331
#endif // LEXY_DSL_OPERATOR_HPP_INCLUDED
313-

tests/lexy/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ set(tests
7575
dsl/member.cpp
7676
dsl/newline.cpp
7777
dsl/operator.cpp
78+
dsl/operator_state.cpp
7879
dsl/option.cpp
7980
dsl/parse_as.cpp
8081
dsl/peek.cpp

tests/lexy/dsl/operator.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,19 @@
1010
// We can't use `lexy::op`, GCC 7 doesn't like it combined with function-local statics.
1111
#define LEXY_OP_OF(Rule) LEXY_DECAY_DECLTYPE(Rule)::op_tag_type
1212

13+
namespace
14+
{
15+
struct tag_with_state
16+
{
17+
const char* pos;
18+
19+
constexpr explicit tag_with_state(const char*) : pos(nullptr) {}
20+
template <typename State>
21+
constexpr explicit tag_with_state(const State&, const char* pos) : pos(pos)
22+
{}
23+
};
24+
} // namespace
25+
1326
TEST_CASE("dsl::op")
1427
{
1528
SUBCASE("token")
@@ -88,6 +101,31 @@ TEST_CASE("dsl::op")
88101
CHECK(success.value == 0);
89102
CHECK(success.trace == test_trace().literal("[").literal("0").literal("]"));
90103
}
104+
SUBCASE("custom tag with state")
105+
{
106+
constexpr auto rule = dsl::op<tag_with_state>(dsl::square_bracketed(LEXY_LIT("0")));
107+
CHECK(std::is_same_v<LEXY_OP_OF(rule), tag_with_state>);
108+
109+
constexpr auto callback = [](const char* pos, LEXY_OP_OF(rule) t) {
110+
CHECK(pos == t.pos);
111+
return 0;
112+
};
113+
114+
auto empty = LEXY_VERIFY("");
115+
CHECK(empty.status == test_result::fatal_error);
116+
CHECK(empty.value == -1);
117+
CHECK(empty.trace == test_trace().expected_literal(0, "[", 0).cancel());
118+
119+
auto token_only = LEXY_VERIFY("[");
120+
CHECK(token_only.status == test_result::fatal_error);
121+
CHECK(token_only.value == -1);
122+
CHECK(token_only.trace == test_trace().literal("[").expected_literal(1, "0", 0).cancel());
123+
124+
auto success = LEXY_VERIFY("[0]");
125+
CHECK(success.status == test_result::success);
126+
CHECK(success.value == 0);
127+
CHECK(success.trace == test_trace().literal("[").literal("0").literal("]"));
128+
}
91129
SUBCASE("custom tag value")
92130
{
93131
constexpr auto rule = dsl::op<42>(dsl::square_bracketed(LEXY_LIT("0")));
@@ -267,4 +305,3 @@ TEST_CASE("dsl::op choice")
267305
CHECK(double_minus.trace == test_trace().literal("-"));
268306
}
269307
}
270-

tests/lexy/dsl/operator_state.cpp

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright (C) 2020-2023 Jonathan Müller and lexy contributors
2+
// SPDX-License-Identifier: BSL-1.0
3+
4+
#include <lexy/dsl/operator.hpp>
5+
6+
#include <doctest/doctest.h>
7+
#include <lexy/action/parse.hpp>
8+
#include <lexy/callback.hpp>
9+
#include <lexy/dsl/expression.hpp>
10+
#include <lexy/dsl/sequence.hpp>
11+
#include <lexy/input/string_input.hpp>
12+
13+
namespace
14+
{
15+
16+
namespace dsl = lexy::dsl;
17+
18+
struct state
19+
{
20+
char const* begin;
21+
std::ptrdiff_t diff;
22+
};
23+
24+
struct tag
25+
{
26+
constexpr tag(state& st, char const* it)
27+
{
28+
st.diff = it - st.begin;
29+
}
30+
};
31+
32+
struct test_finish : lexy::expression_production
33+
{
34+
static constexpr auto atom = LEXY_LIT("x");
35+
36+
struct operation : dsl::prefix_op
37+
{
38+
static constexpr auto op = dsl::op<tag>(LEXY_LIT("-"));
39+
using operand = dsl::atom;
40+
};
41+
42+
static constexpr auto value = lexy::noop;
43+
};
44+
45+
struct test_bp
46+
{
47+
static constexpr auto rule = dsl::op<tag>(LEXY_LIT("-")) | dsl::op<tag>(LEXY_LIT("+"));
48+
static constexpr auto value = lexy::forward<tag>;
49+
};
50+
51+
struct test_p
52+
{
53+
static constexpr auto rule = dsl::op<tag>(LEXY_LIT("-"));
54+
static constexpr auto value = lexy::forward<tag>;
55+
};
56+
57+
} // namespace
58+
59+
TEST_CASE("dsl::op_state")
60+
{
61+
SUBCASE("finish")
62+
{
63+
auto const* str = "-x";
64+
auto input = lexy::zstring_input(str);
65+
auto st = state{input.reader().position(), -1};
66+
auto ret = lexy::parse<test_finish>(input, st, lexy::noop);
67+
CHECK(ret.is_success());
68+
CHECK(st.diff == 0);
69+
}
70+
SUBCASE("bp")
71+
{
72+
auto const* str = "-";
73+
auto input = lexy::zstring_input(str);
74+
auto st = state{input.reader().position(), -1};
75+
auto ret = lexy::parse<test_bp>(input, st, lexy::noop);
76+
CHECK(ret.is_success());
77+
CHECK(st.diff == 0);
78+
}
79+
SUBCASE("p")
80+
{
81+
auto const* str = "-";
82+
auto input = lexy::zstring_input(str);
83+
auto st = state{input.reader().position(), -1};
84+
auto ret = lexy::parse<test_p>(input, st, lexy::noop);
85+
CHECK(ret.is_success());
86+
CHECK(st.diff == 0);
87+
}
88+
}

0 commit comments

Comments
 (0)