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
|
package bjc.utils.parserutils.pattern;
import java.util.StringJoiner;
import java.util.function.Supplier;
import java.util.regex.*;
/**
* Builder interface for regex patterns.
*
* @author bjculkin
*
*/
public interface PatternPart {
/**
* Convert this pattern part into a regex.
*
* @return The regex this part represents.
*/
public String toRegex();
public boolean canOptimize();
static PatternPart part(boolean canOptimize, Supplier<String> func) {
return new PatternPart() {
@Override
public String toRegex() {
return func.get();
}
@Override
public boolean canOptimize() {
return canOptimize;
}
};
}
static PatternPart var(Supplier<String> source) {
return part(false, source);
}
/**
* Create a 'raw' pattern part, which just echoes the given string.
*
* @param str The regex to include
*
* @return A pattern part which converts to the given string.
*/
static PatternPart raw(String str) {
return part(true, () -> str);
}
static PatternPart joining(String joiner, PatternPart... parts) {
return new PatternPart() {
@Override
public String toRegex() {
StringJoiner sj = new StringJoiner(joiner);
for (PatternPart part : parts) sj.add(part.toRegex());
return sj.toString();
}
@Override
public boolean canOptimize() {
for (PatternPart part : parts)
if (!part.canOptimize()) return false;
return true;
}
};
}
/**
* Create a pattern part which matches the given string.
*
* @param str The string to match
*
* @return A pattern which matches the given string.
*/
static PatternPart literal(String str) {
return part(true, () -> Pattern.quote(str));
}
/**
* Create a pattern part which matches a single digit.
*
* @return A pattern that matches a digit.
*/
static PatternPart digit() {
return raw("\\d");
}
static PatternPart cclass(char... chars) {
return part(true, () -> {
StringBuilder sb = new StringBuilder("[");
for (char ch : chars) sb.append(ch);
sb.append("]");
return sb.toString();
});
}
static PatternPart notCClass(char... chars) {
return part(true, () -> {
StringBuilder sb = new StringBuilder("[^");
for (char ch : chars) sb.append(ch);
sb.append("]");
return sb.toString();
});
}
static PatternPart nonspace() {
return raw("\\S");
}
static PatternPart concat(PatternPart... parts) {
return joining(" ", parts);
}
static PatternPart alternate(PatternPart... parts) {
return joining("|", parts);
}
static PatternPart repeat(PatternPart part) {
return part(part.canOptimize(), () -> part.toRegex() + "*");
}
static PatternPart optional(PatternPart part) {
return part(part.canOptimize(), () -> part.toRegex() + "?");
}
static PatternPart repeatAtLeastOnce(PatternPart part) {
return part(part.canOptimize(), () -> part.toRegex() + "*");
}
static PatternPart surround(String lhs, String rhs, PatternPart part) {
return part(part.canOptimize(), () -> lhs + part.toRegex() + rhs);
}
static PatternPart group(PatternPart part) {
return surround("(", ")", part);
}
static PatternPart namedGroup(String groupName, PatternPart part) {
return surround("(<" + groupName + ">", ")", part);
}
static PatternPart nonCaptureGroup(PatternPart part) {
return surround("(?:", ")", part);
}
}
|