Skip to content

Commit 3d95f70

Browse files
author
Hana Dusíková
committed
cnttp support squashed
1 parent 7eff180 commit 3d95f70

25 files changed

+3774
-3498
lines changed

Makefile copy

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
.PHONY: default all clean grammar compare
2+
3+
default: all
4+
5+
TARGETS := result.cpp test.cpp $(wildcard tests/benchmark-exec/*.cpp)
6+
IGNORE := $(wildcard tests/benchmark/*.cpp) $(wildcard tests/benchmark-exec/*.cpp)
7+
8+
DESATOMAT := /www/root/desatomat/console/desatomat.php
9+
10+
CPP_STANDARD := $(shell ./cpp-20-check.sh $(CXX))
11+
12+
CXXFLAGS := $(CPP_STANDARD) -Iinclude -O3 -Wno-gnu-string-literal-operator-template -pedantic -Wall -Wextra
13+
LDFLAGS :=
14+
15+
TESTS := $(wildcard tests/*.cpp) $(wildcard tests/benchmark/*.cpp)
16+
TRUE_TARGETS := $(TARGETS:%.cpp=%)
17+
override TRUE_TARGETS := $(filter-out $(IGNORE:%.cpp=%), $(TRUE_TARGETS))
18+
OBJECTS := $(TARGETS:%.cpp=%.o) $(TESTS:%.cpp=%.o)
19+
override OBJECTS := $(filter-out $(IGNORE:%.cpp=%.o),$(OBJECTS))
20+
DEPEDENCY_FILES := $(OBJECTS:%.o=%.d)
21+
22+
all: $(TRUE_TARGETS) $(OBJECTS)
23+
24+
list:
25+
echo $(SUPPORTED_CPP20)
26+
27+
$(TRUE_TARGETS): %: %.o
28+
$(CXX) $< $(LDFLAGS) -o $@
29+
30+
$(OBJECTS): %.o: %.cpp
31+
$(CXX) $(CXXFLAGS) -MMD -c $< -o $@
32+
33+
-include $(DEPEDENCY_FILES)
34+
35+
benchmark:
36+
@$(MAKE) clean
37+
@$(MAKE) IGNORE=""
38+
39+
benchmark-clean:
40+
@$(MAKE) IGNORE="" clean
41+
42+
clean:
43+
rm -f $(TRUE_TARGETS) $(OBJECTS) $(DEPEDENCY_FILES) mtent12.txt mtent12.zip
44+
45+
grammar: include/ctre/pcre.hpp
46+
47+
regrammar:
48+
@rm -f include/ctre/pcre.hpp
49+
@$(MAKE) grammar
50+
51+
include/ctre/pcre.hpp: include/ctre/pcre.gram
52+
@echo "LL1q $<"
53+
@$(DESATOMAT) --ll --q --input=include/ctre/pcre.gram --output=include/ctre/ --generator=cpp_ctll_v2 --cfg:fname=pcre.hpp --cfg:namespace=ctre --cfg:guard=CTRE__PCRE__HPP --cfg:grammar_name=pcre
54+
55+
mtent12.zip:
56+
curl -s http://www.gutenberg.org/files/3200/old/mtent12.zip -o mtent12.zip
57+
58+
mtent12.txt: mtent12.zip
59+
unzip -o mtent12.zip
60+
touch mtent12.txt
61+
62+
REPEAT:=10
63+
64+
compare: mtent12.txt
65+
$(CXX) $(CXXFLAGS) -MMD -march=native -DPATTERN="\"(${PATTERN})\"" -c tests/benchmark-range/measurement.cpp -o tests/benchmark-range/measurement.o
66+
$(CXX) tests/benchmark-range/measurement.o -lboost_regex -lpcre2-8 -lre2 -o tests/benchmark-range/measurement
67+
tests/benchmark-range/measurement all mtent12.txt ${REPEAT}

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ ctre::match<"REGEX">(subject); // C++20
2424

2525
* clang 5.0+ (template UDL, C++17 syntax)
2626
* gcc 7.2+ (template UDL, C++17 syntax)
27-
* gcc 9.0+ (C++17 & C++20 cNTTP syntax)
27+
* gcc 9.0+ (C++17 & C++20 cNTTP syntax, trampolining a.k.a. long patterns are not supported due compiler bug)
2828
* MSVC 15.8.8+ (C++17 syntax only)
2929

3030
#### Template UDL syntax
@@ -47,7 +47,7 @@ constexpr auto match(std::string_view sv) noexcept {
4747

4848
#### C++20 syntax
4949

50-
Currently only compiler which supports cNTTP syntax `ctre::match<PATTERN>(subject)` is GCC 9+.
50+
Currently only compiler which supports cNTTP syntax `ctre::match<PATTERN>(subject)` is GCC 9+. Use `-DEXPERIMENTAL_GCC_9` flag to disable trampolining (which crash compiler on ICE).
5151

5252
## Examples
5353

cpp-20-check.sh

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env bash
2+
if echo "" | $1 -std=c++2a -E -x c++ - 2>/dev/null 1>/dev/null; then
3+
VERSION=`$1 -v 2>&1 | grep "gcc" | grep "experimental"`
4+
if [ -z "$VERSION" ]; then
5+
echo "-std=c++2a";
6+
else
7+
echo "-std=c++2a -DEXPERIMENTAL_GCC_9";
8+
fi
9+
else
10+
echo "-std=c++17";
11+
fi

include/ctll/fixed_string.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ template <typename CharT> class basic_fixed_string<CharT, 0> {
5353
};
5454

5555
template <typename CharT, size_t N> basic_fixed_string(const CharT (&)[N]) -> basic_fixed_string<CharT, N>;
56+
template <typename CharT, size_t N> basic_fixed_string(basic_fixed_string<CharT, N>) -> basic_fixed_string<CharT, N>;
5657

5758
}
5859

include/ctll/grammars.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ template <typename Grammar> struct augment_grammar: public Grammar {
9595
static constexpr auto rule(...) -> ctll::reject;
9696

9797
// start stack is just a list<Grammar::_start>;
98-
static constexpr inline auto start_stack = list<typename Grammar::_start>{};
98+
using start_stack = list<typename Grammar::_start>;
9999
};
100100

101101

include/ctll/parser.hpp

+76-45
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "list.hpp"
66
#include "grammars.hpp"
77
#include "actions.hpp"
8+
89
#include <limits>
910

1011
namespace ctll {
@@ -16,19 +17,47 @@ enum class decision {
1617
undecided
1718
};
1819

19-
template <typename T> void id(T);
20-
2120
struct placeholder { };
2221

22+
template <size_t> using index_placeholder = placeholder;
23+
24+
#ifdef EXPERIMENTAL_GCC_9
25+
template <size_t, typename, typename Subject, decision Decision> struct results {
26+
constexpr operator bool() const noexcept {
27+
return Decision == decision::accept;
28+
}
29+
using output_type = Subject;
30+
};
31+
#endif
32+
33+
2334
#if !__cpp_nontype_template_parameter_class
2435
template <typename Grammar, const auto & input, typename ActionSelector = empty_actions, bool IgnoreUnknownActions = false> struct parser {
2536
#else
26-
template <typename Grammar, basic_fixed_string input, typename ActionSelector = empty_actions, bool IgnoreUnknownActions = true> struct parser { // in c++20
37+
template <typename Grammar, ctll::basic_fixed_string input, typename ActionSelector = empty_actions, bool IgnoreUnknownActions = false> struct parser { // in c++20
2738
#endif
2839
using Actions = ctll::conditional<IgnoreUnknownActions, ignore_unknown<ActionSelector>, identity<ActionSelector>>;
29-
static inline constexpr auto grammar = augment_grammar<Grammar>();
40+
using grammar = augment_grammar<Grammar>;
3041

31-
template <size_t Pos, typename Stack = void, typename Subject = void, decision Decision = decision::undecided> struct seed;
42+
#ifndef EXPERIMENTAL_GCC_9
43+
template <size_t Pos, typename Stack, typename Subject, decision Decision> struct results {
44+
constexpr inline CTLL_FORCE_INLINE operator bool() const noexcept {
45+
return Decision == decision::accept;
46+
}
47+
48+
using output_type = Subject;
49+
50+
constexpr auto operator+(placeholder) const noexcept {
51+
if constexpr (Decision == decision::undecided) {
52+
// parse for current char (RPos) with previous stack and subject :)
53+
return decide<Pos, Stack, Subject>({}, {});
54+
} else {
55+
// if there is decision already => just push it to the end of fold expression
56+
return *this;
57+
}
58+
}
59+
};
60+
#endif
3261

3362
template <size_t Pos> static constexpr auto get_current_term() noexcept {
3463
if constexpr (Pos < input.size()) {
@@ -38,6 +67,7 @@ template <typename Grammar, basic_fixed_string input, typename ActionSelector =
3867
} else {
3968
return term<input[Pos]>{};
4069
}
70+
4171
} else {
4272
// return epsilon if we are past the input
4373
return epsilon{};
@@ -48,29 +78,38 @@ template <typename Grammar, basic_fixed_string input, typename ActionSelector =
4878
// there is no previous character on input if we are on start
4979
return epsilon{};
5080
} else if constexpr ((Pos-1) < input.size()) {
51-
return term<input[Pos-1]>{};
81+
constexpr auto value = input[Pos-1];
82+
if constexpr (value <= std::numeric_limits<char>::max()) {
83+
return term<static_cast<char>(value)>{};
84+
} else {
85+
return term<input[Pos]>{};
86+
}
5287
} else {
5388
return epsilon{};
5489
}
5590
}
5691
// if rule is accept => return true and subject
5792
template <size_t Pos, typename Terminal, typename Stack, typename Subject>
5893
static constexpr auto move(ctll::accept, Terminal, Stack, Subject) noexcept {
59-
return seed<Pos, Stack, Subject, decision::accept>();
94+
return results<Pos, Stack, Subject, decision::accept>();
6095
}
6196
// if rule is reject => return false and subject
6297
template <size_t Pos, typename Terminal, typename Stack, typename Subject>
6398
static constexpr auto move(ctll::reject, Terminal, Stack, Subject) noexcept {
64-
return seed<Pos, Stack, Subject, decision::reject>();
99+
return results<Pos, Stack, Subject, decision::reject>();
65100
}
66101
// if rule is pop_input => move to next character
67102
template <size_t Pos, typename Terminal, typename Stack, typename Subject>
68103
static constexpr auto move(ctll::pop_input, Terminal, Stack, Subject) noexcept {
69-
return seed<Pos+1, Stack, Subject, decision::undecided>();
104+
#ifdef EXPERIMENTAL_GCC_9
105+
return decide<Pos+1>(Stack(), Subject());
106+
#else
107+
return results<Pos+1, Stack, Subject, decision::undecided>();
108+
#endif
70109
}
71110
// if rule is string => push it to the front of stack
72111
template <size_t Pos, typename... Content, typename Terminal, typename Stack, typename Subject>
73-
static constexpr auto move(ctll::push<Content...> string, Terminal, Stack stack, Subject subject) noexcept {
112+
static constexpr auto move(push<Content...> string, Terminal, Stack stack, Subject subject) noexcept {
74113
return decide<Pos>(push_front(string, stack), subject);
75114
}
76115
// if rule is epsilon (empty string) => continue
@@ -81,14 +120,22 @@ template <typename Grammar, basic_fixed_string input, typename ActionSelector =
81120
// if rule is string with current character at the beginning (term<V>) => move to next character
82121
// and push string without the character (quick LL(1))
83122
template <size_t Pos, auto V, typename... Content, typename Stack, typename Subject>
84-
static constexpr auto move(ctll::push<term<V>, Content...>, term<V>, Stack stack, Subject) noexcept {
85-
return seed<Pos+1, decltype(push_front(list<Content...>(), stack)), Subject, decision::undecided>();
123+
static constexpr auto move(push<term<V>, Content...>, term<V>, Stack stack, Subject) noexcept {
124+
#ifdef EXPERIMENTAL_GCC_9
125+
return decide<Pos+1>(push_front(list<Content...>(), stack), Subject());
126+
#else
127+
return results<Pos+1, decltype(push_front(list<Content...>(), stack)), Subject, decision::undecided>();
128+
#endif
86129
}
87130
// if rule is string with any character at the beginning (compatible with current term<T>) => move to next character
88131
// and push string without the character (quick LL(1))
89132
template <size_t Pos, auto V, typename... Content, auto T, typename Stack, typename Subject>
90-
static constexpr auto move(ctll::push<anything, Content...>, term<T>, Stack stack, Subject) noexcept {
91-
return seed<Pos+1, decltype(push_front(list<Content...>(), stack)), Subject, decision::undecided>();
133+
static constexpr auto move(push<anything, Content...>, term<T>, Stack stack, Subject) noexcept {
134+
#ifdef EXPERIMENTAL_GCC_9
135+
return decide<Pos+1>(push_front(list<Content...>(), stack), Subject());
136+
#else
137+
return results<Pos+1, decltype(push_front(list<Content...>(), stack)), Subject, decision::undecided>();
138+
#endif
92139
}
93140
// decide if we need to take action or move
94141
template <size_t Pos, typename Stack, typename Subject> static constexpr auto decide(Stack previous_stack, Subject previous_subject) noexcept {
@@ -103,50 +150,28 @@ template <typename Grammar, basic_fixed_string input, typename ActionSelector =
103150

104151
// in case that semantic action is error => reject input
105152
if constexpr (std::is_same_v<ctll::reject, decltype(subject)>) {
106-
return seed<Pos, Stack, Subject, decision::reject>();
153+
#ifndef EXPERIMENTAL_GCC_9
154+
return results<Pos, Stack, Subject, decision::reject>();
155+
#else
156+
return results<Pos, Stack, Subject, decision::reject>();
157+
#endif
107158
} else {
108159
return decide<Pos>(stack, subject);
109160
}
110161
} else {
111162
// all other cases are ordinary for LL(1) parser
112163
auto current_term = get_current_term<Pos>();
113-
auto rule = decltype(grammar.rule(top_symbol,current_term))();
164+
auto rule = decltype(grammar::rule(top_symbol,current_term))();
114165
return move<Pos>(rule, current_term, stack, previous_subject);
115166
}
116167
}
117-
// helper type for trampoline
118-
119-
template <size_t Pos, typename Stack, typename Subject, decision Decision> struct seed {
120-
constexpr inline CTLL_FORCE_INLINE operator bool() const noexcept {
121-
return Decision == decision::accept;
122-
}
123-
124-
using output_type = Subject;
125-
126-
static constexpr auto parse() noexcept {
127-
// push current position to decide function with current stack and subject
128-
return decide<Pos>(Stack{}, Subject{});
129-
}
130-
131-
constexpr auto operator+(placeholder) const noexcept {
132-
if constexpr (Decision == decision::undecided) {
133-
// parse for current char (RPos) with previous stack and subject :)
134-
return decltype(seed<Pos, Stack, Subject, Decision>::parse()){};
135-
} else {
136-
// if there is decision already => just push it to the end of fold expression
137-
return *this;
138-
}
139-
}
140-
};
141-
142-
template <size_t> using index_placeholder = placeholder;
143168

169+
#ifndef EXPERIMENTAL_GCC_9
144170
// trampolines with folded expression
145171
template <typename Subject, size_t... Pos> static constexpr auto trampoline_decide(Subject, std::index_sequence<Pos...>) noexcept {
146172
// parse everything for first char and than for next and next ...
147173
// Pos+1 is needed as we want to finish calculation with epsilons on stack
148-
auto v = (seed<0, decltype(grammar.start_stack), Subject, decision::undecided>::parse() + ... + index_placeholder<Pos+1>());
149-
//id(v);
174+
auto v = (decide<0, typename grammar::start_stack, Subject>({}, {}) + ... + index_placeholder<Pos+1>());
150175
return v;
151176
}
152177

@@ -156,8 +181,14 @@ template <typename Grammar, basic_fixed_string input, typename ActionSelector =
156181
}
157182

158183
template <typename Subject = empty_subject> using output = decltype(trampoline_decide<Subject>());
159-
static inline constexpr bool correct = trampoline_decide(empty_subject());
184+
static inline constexpr bool correct = trampoline_decide<empty_subject>();
160185
template <typename Subject = empty_subject> static inline constexpr bool correct_with = trampoline_decide<Subject>();
186+
#else
187+
template <typename Subject = empty_subject> using output = decltype(decide<0, typename grammar::start_stack, Subject>({}, {}));
188+
static inline constexpr bool correct = decide<0, typename grammar::start_stack, empty_subject>({}, {});
189+
template <typename Subject = empty_subject> static inline constexpr bool correct_with = decide<0, typename grammar::start_stack, Subject>({}, {});
190+
#endif
191+
161192
};
162193

163194
} // end of ctll namespace

0 commit comments

Comments
 (0)