/src/hobbes/include/hobbes/lang/pat/pattern.H
Line | Count | Source |
1 | | /* |
2 | | * pattern : defines a high-level pattern matching expression |
3 | | */ |
4 | | |
5 | | #ifndef HOBBES_LANG_PAT_PATTERN_HPP_INCLUDED |
6 | | #define HOBBES_LANG_PAT_PATTERN_HPP_INCLUDED |
7 | | |
8 | | #include <hobbes/lang/expr.H> |
9 | | #include <hobbes/lang/pat/regex.H> |
10 | | #include <hobbes/util/str.H> |
11 | | #include <hobbes/util/ptr.H> |
12 | | #include <hobbes/util/hash.H> |
13 | | #include <hobbes/util/lannotation.H> |
14 | | |
15 | | namespace hobbes { |
16 | | |
17 | | using Idxs = std::vector<size_t>; |
18 | | |
19 | | // the main signature for this module -- convert a pattern match expression to a primitive compilable expression |
20 | | class Pattern; |
21 | | using PatternPtr = std::shared_ptr<Pattern>; |
22 | | using Patterns = std::vector<PatternPtr>; |
23 | | |
24 | | struct PatternRow { |
25 | 10 | inline PatternRow() = default; |
26 | 749 | inline PatternRow(const Patterns& ps, const ExprPtr& r) : patterns(ps), result(r) { } |
27 | 1.26k | inline PatternRow(const Patterns& ps, const ExprPtr& g, const ExprPtr& r) : patterns(ps), guard(g), result(r) { } |
28 | | |
29 | | Patterns patterns; // match conditions for this row |
30 | | ExprPtr guard; // a required condition for a match (may be null if there is no guard) |
31 | | ExprPtr result; // the expression to be evaluated when the given pattern conditions are met |
32 | | }; |
33 | | using PatternRows = std::vector<PatternRow>; |
34 | | using UnreachableMatchRowsPtr = std::shared_ptr<std::vector<std::pair<size_t, PatternRow>>>; |
35 | | |
36 | | // support quick hashing of pattern tables |
37 | | bool operator==(const PatternRow&, const PatternRow&); |
38 | | size_t hash(const PatternRow&); |
39 | | |
40 | | // sneak the whole compiler through a back door to see how bindings are set up and maybe to do low-level compilation for special cases of pattern matches |
41 | | class cc; |
42 | | |
43 | | // evaluate the expression for the first pattern row matching the input |
44 | | ExprPtr compileMatch(cc*, const Exprs&, const PatternRows&, const LexicalAnnotation&); |
45 | | |
46 | | // produce an expression to test whether an expression matches a single pattern |
47 | | ExprPtr compileMatchTest(cc*, const ExprPtr&, const PatternPtr&, const LexicalAnnotation&); |
48 | | |
49 | | // produce an expression to test whether (and/or how) a regex matches a string |
50 | | ExprPtr compileRegexFn(cc*, const std::string& regex, const LexicalAnnotation&); |
51 | | |
52 | | // a general representation of patterns to match |
53 | | class Pattern : public LexicallyAnnotated { |
54 | | public: |
55 | 1.05k | virtual ~Pattern() = default; |
56 | | virtual void show(std::ostream&) const = 0; |
57 | | virtual bool operator==(const Pattern&) const = 0; |
58 | | |
59 | | // allow explicit naming of stages of a pattern (for internal use only) |
60 | | const std::string& name() const; |
61 | | void name(const std::string&); |
62 | | private: |
63 | | std::string pname; |
64 | | protected: |
65 | | virtual void assignSubNames(const std::string&) = 0; |
66 | | |
67 | | // improves performance of case-analysis over instances (to avoid 'dynamic_cast') |
68 | | public: |
69 | | int case_id() const; |
70 | | protected: |
71 | | Pattern(int cid, const LexicalAnnotation&); |
72 | | private: |
73 | | int cid; |
74 | | }; |
75 | | |
76 | | template <typename Case> |
77 | | class PatternCase : public Pattern { |
78 | | public: |
79 | | using Base = PatternCase<Case>; |
80 | | PatternCase(const LexicalAnnotation&); |
81 | | }; |
82 | | |
83 | | std::string show(const PatternPtr& p); |
84 | | std::string show(const Patterns& ps); |
85 | | std::string show(const PatternRow& pr); |
86 | | |
87 | | // literal patterns |
88 | | // contain a "proxy value" for internal equality/ordering checks: ((), false, 0xde, 'c', 1, 3L, 2.3, ...) |
89 | | // contain a "residual expression value" for generating code |
90 | | // (this distinction allows us to use aliased constants like exchanges, timespans, etc in match expressions) |
91 | | class MatchLiteral : public PatternCase<MatchLiteral> { |
92 | | public: |
93 | | MatchLiteral(const PrimitivePtr& proxy, const LexicalAnnotation&); |
94 | | MatchLiteral(const PrimitivePtr& proxy, const ExprPtr& value, const LexicalAnnotation&); |
95 | | |
96 | | const ExprPtr& expression() const; |
97 | | const PrimitivePtr& equivConstant() const; |
98 | | |
99 | | void show(std::ostream&) const override; |
100 | | bool operator==(const Pattern&) const override; |
101 | | |
102 | | static const int type_case_id = 0; |
103 | | private: |
104 | | PrimitivePtr p; |
105 | | ExprPtr e; |
106 | | |
107 | | void assignSubNames(const std::string&) override; |
108 | | }; |
109 | | |
110 | | // match any value, maybe bind it to a variable name |
111 | | class MatchAny : public PatternCase<MatchAny> { |
112 | | public: |
113 | | MatchAny(const std::string& vn, const LexicalAnnotation&); |
114 | | |
115 | | const std::string& value() const; |
116 | | |
117 | | void show(std::ostream&) const override; |
118 | | bool operator==(const Pattern&) const override; |
119 | | |
120 | | static const int type_case_id = 1; |
121 | | private: |
122 | | std::string vn; |
123 | | |
124 | | void assignSubNames(const std::string&) override; |
125 | | }; |
126 | | |
127 | | // match an array of patterns |
128 | | class MatchArray : public PatternCase<MatchArray> { |
129 | | public: |
130 | | MatchArray(const Patterns& ps, const LexicalAnnotation&); // default indexes = [0,ps.size()) |
131 | | |
132 | | const PatternPtr& pattern(size_t i) const; |
133 | | const Idxs& indexes() const; |
134 | | size_t size() const; // alias for indexes().size() |
135 | | |
136 | | // allow external permutation of array match sequence |
137 | | void indexes(const Idxs&); |
138 | | |
139 | | void show(std::ostream&) const override; |
140 | | bool operator==(const Pattern&) const override; |
141 | | |
142 | | static const int type_case_id = 2; |
143 | | private: |
144 | | Patterns ps; |
145 | | Idxs idxs; |
146 | | |
147 | | void assignSubNames(const std::string&) override; |
148 | | }; |
149 | | |
150 | | // match a regular expression |
151 | | class MatchRegex : public PatternCase<MatchRegex> { |
152 | | public: |
153 | | MatchRegex(const RegexPtr&, const LexicalAnnotation&); |
154 | | MatchRegex(const std::string&, const LexicalAnnotation&); |
155 | | |
156 | | std::string text() const; |
157 | | const RegexPtr& value() const; |
158 | | |
159 | | static PatternPtr toRegex(const MatchArray&); |
160 | | |
161 | | void show(std::ostream&) const override; |
162 | | bool operator==(const Pattern&) const override; |
163 | | static const int type_case_id = 3; |
164 | | private: |
165 | | RegexPtr regex; |
166 | | |
167 | | void assignSubNames(const std::string&) override; |
168 | | }; |
169 | | |
170 | | // match a record of patterns |
171 | | class MatchRecord : public PatternCase<MatchRecord> { |
172 | | public: |
173 | | using Field = std::pair<std::string, PatternPtr>; |
174 | | using Fields = std::vector<Field>; |
175 | | |
176 | | MatchRecord(const Fields& fs, const LexicalAnnotation&); // default indexes = [0,fs.size()) |
177 | | |
178 | | const Field& pattern(size_t i) const; |
179 | | const Idxs& indexes() const; |
180 | | size_t size() const; // alias for indexes().size() |
181 | | |
182 | | const Fields& fields() const; |
183 | | void fields(const Fields&); |
184 | | |
185 | | void show(std::ostream&) const override; |
186 | | bool operator==(const Pattern&) const override; |
187 | | |
188 | | static const int type_case_id = 4; |
189 | | private: |
190 | | Fields fs; |
191 | | Idxs is; |
192 | | |
193 | | void assignSubNames(const std::string&) override; |
194 | | |
195 | | static void show(std::ostream&, const Field& f); |
196 | | }; |
197 | | |
198 | | // match a variant constructor |
199 | | class MatchVariant : public PatternCase<MatchVariant> { |
200 | | public: |
201 | | MatchVariant(const std::string& lbl, const PatternPtr& p, const LexicalAnnotation&); |
202 | | |
203 | | const std::string& label() const; |
204 | | const PatternPtr& value() const; |
205 | | |
206 | | void show(std::ostream&) const override; |
207 | | bool operator==(const Pattern&) const override; |
208 | | |
209 | | static const int type_case_id = 5; |
210 | | private: |
211 | | std::string lbl; |
212 | | PatternPtr p; |
213 | | |
214 | | void assignSubNames(const std::string&) override; |
215 | | }; |
216 | | |
217 | | template <typename Case> |
218 | 1.05k | PatternCase<Case>::PatternCase(const LexicalAnnotation& la) : Pattern(Case::type_case_id, la) { |
219 | 1.05k | } hobbes::PatternCase<hobbes::MatchLiteral>::PatternCase(hobbes::LexicalAnnotation const&) Line | Count | Source | 218 | 29 | PatternCase<Case>::PatternCase(const LexicalAnnotation& la) : Pattern(Case::type_case_id, la) { | 219 | 29 | } |
hobbes::PatternCase<hobbes::MatchAny>::PatternCase(hobbes::LexicalAnnotation const&) Line | Count | Source | 218 | 869 | PatternCase<Case>::PatternCase(const LexicalAnnotation& la) : Pattern(Case::type_case_id, la) { | 219 | 869 | } |
hobbes::PatternCase<hobbes::MatchArray>::PatternCase(hobbes::LexicalAnnotation const&) Line | Count | Source | 218 | 12 | PatternCase<Case>::PatternCase(const LexicalAnnotation& la) : Pattern(Case::type_case_id, la) { | 219 | 12 | } |
hobbes::PatternCase<hobbes::MatchRegex>::PatternCase(hobbes::LexicalAnnotation const&) Line | Count | Source | 218 | 8 | PatternCase<Case>::PatternCase(const LexicalAnnotation& la) : Pattern(Case::type_case_id, la) { | 219 | 8 | } |
hobbes::PatternCase<hobbes::MatchRecord>::PatternCase(hobbes::LexicalAnnotation const&) Line | Count | Source | 218 | 44 | PatternCase<Case>::PatternCase(const LexicalAnnotation& la) : Pattern(Case::type_case_id, la) { | 219 | 44 | } |
hobbes::PatternCase<hobbes::MatchVariant>::PatternCase(hobbes::LexicalAnnotation const&) Line | Count | Source | 218 | 89 | PatternCase<Case>::PatternCase(const LexicalAnnotation& la) : Pattern(Case::type_case_id, la) { | 219 | 89 | } |
|
220 | | |
221 | | // destruction-side for high-level pattern representations |
222 | | template <typename T> |
223 | | struct switchPattern { |
224 | | virtual T with(const MatchLiteral*) const = 0; |
225 | | virtual T with(const MatchAny*) const = 0; |
226 | | virtual T with(const MatchArray*) const = 0; |
227 | | virtual T with(const MatchRegex*) const = 0; |
228 | | virtual T with(const MatchRecord*) const = 0; |
229 | | virtual T with(const MatchVariant*) const = 0; |
230 | | }; |
231 | | |
232 | | template <typename T> |
233 | 3.95k | T switchOf(const PatternPtr& p, const switchPattern<T>& f) { |
234 | 3.95k | switch (p->case_id()) { |
235 | 133 | case MatchLiteral::type_case_id: |
236 | 133 | return f.with(rcast<const MatchLiteral*>(p.get())); |
237 | 3.05k | case MatchAny::type_case_id: |
238 | 3.05k | return f.with(rcast<const MatchAny*>(p.get())); |
239 | 48 | case MatchArray::type_case_id: |
240 | 48 | return f.with(rcast<const MatchArray*>(p.get())); |
241 | 9 | case MatchRegex::type_case_id: |
242 | 9 | return f.with(rcast<const MatchRegex*>(p.get())); |
243 | 231 | case MatchRecord::type_case_id: |
244 | 231 | return f.with(rcast<const MatchRecord*>(p.get())); |
245 | 471 | case MatchVariant::type_case_id: |
246 | 471 | return f.with(rcast<const MatchVariant*>(p.get())); |
247 | 0 | default: |
248 | 0 | throw annotated_error(*p, "Internal error, cannot switch on unknown high-level pattern: " + show(p)); |
249 | 3.95k | } |
250 | 3.95k | } std::shared_ptr<hobbes::MonoType> hobbes::switchOf<std::shared_ptr<hobbes::MonoType> >(std::shared_ptr<hobbes::Pattern> const&, hobbes::switchPattern<std::shared_ptr<hobbes::MonoType> > const&) Line | Count | Source | 233 | 900 | T switchOf(const PatternPtr& p, const switchPattern<T>& f) { | 234 | 900 | switch (p->case_id()) { | 235 | 25 | case MatchLiteral::type_case_id: | 236 | 25 | return f.with(rcast<const MatchLiteral*>(p.get())); | 237 | 754 | case MatchAny::type_case_id: | 238 | 754 | return f.with(rcast<const MatchAny*>(p.get())); | 239 | 12 | case MatchArray::type_case_id: | 240 | 12 | return f.with(rcast<const MatchArray*>(p.get())); | 241 | 2 | case MatchRegex::type_case_id: | 242 | 2 | return f.with(rcast<const MatchRegex*>(p.get())); | 243 | 19 | case MatchRecord::type_case_id: | 244 | 19 | return f.with(rcast<const MatchRecord*>(p.get())); | 245 | 88 | case MatchVariant::type_case_id: | 246 | 88 | return f.with(rcast<const MatchVariant*>(p.get())); | 247 | 0 | default: | 248 | 0 | throw annotated_error(*p, "Internal error, cannot switch on unknown high-level pattern: " + show(p)); | 249 | 900 | } | 250 | 900 | } |
unsigned char hobbes::switchOf<unsigned char>(std::shared_ptr<hobbes::Pattern> const&, hobbes::switchPattern<unsigned char> const&) Line | Count | Source | 233 | 2.08k | T switchOf(const PatternPtr& p, const switchPattern<T>& f) { | 234 | 2.08k | switch (p->case_id()) { | 235 | 50 | case MatchLiteral::type_case_id: | 236 | 50 | return f.with(rcast<const MatchLiteral*>(p.get())); | 237 | 1.73k | case MatchAny::type_case_id: | 238 | 1.73k | return f.with(rcast<const MatchAny*>(p.get())); | 239 | 24 | case MatchArray::type_case_id: | 240 | 24 | return f.with(rcast<const MatchArray*>(p.get())); | 241 | 4 | case MatchRegex::type_case_id: | 242 | 4 | return f.with(rcast<const MatchRegex*>(p.get())); | 243 | 88 | case MatchRecord::type_case_id: | 244 | 88 | return f.with(rcast<const MatchRecord*>(p.get())); | 245 | 178 | case MatchVariant::type_case_id: | 246 | 178 | return f.with(rcast<const MatchVariant*>(p.get())); | 247 | 0 | default: | 248 | 0 | throw annotated_error(*p, "Internal error, cannot switch on unknown high-level pattern: " + show(p)); | 249 | 2.08k | } | 250 | 2.08k | } |
bool hobbes::switchOf<bool>(std::shared_ptr<hobbes::Pattern> const&, hobbes::switchPattern<bool> const&) Line | Count | Source | 233 | 187 | T switchOf(const PatternPtr& p, const switchPattern<T>& f) { | 234 | 187 | switch (p->case_id()) { | 235 | 6 | case MatchLiteral::type_case_id: | 236 | 6 | return f.with(rcast<const MatchLiteral*>(p.get())); | 237 | 173 | case MatchAny::type_case_id: | 238 | 173 | return f.with(rcast<const MatchAny*>(p.get())); | 239 | 0 | case MatchArray::type_case_id: | 240 | 0 | return f.with(rcast<const MatchArray*>(p.get())); | 241 | 0 | case MatchRegex::type_case_id: | 242 | 0 | return f.with(rcast<const MatchRegex*>(p.get())); | 243 | 7 | case MatchRecord::type_case_id: | 244 | 7 | return f.with(rcast<const MatchRecord*>(p.get())); | 245 | 1 | case MatchVariant::type_case_id: | 246 | 1 | return f.with(rcast<const MatchVariant*>(p.get())); | 247 | 0 | default: | 248 | 0 | throw annotated_error(*p, "Internal error, cannot switch on unknown high-level pattern: " + show(p)); | 249 | 187 | } | 250 | 187 | } |
std::shared_ptr<hobbes::MState> hobbes::switchOf<std::shared_ptr<hobbes::MState> >(std::shared_ptr<hobbes::Pattern> const&, hobbes::switchPattern<std::shared_ptr<hobbes::MState> > const&) Line | Count | Source | 233 | 131 | T switchOf(const PatternPtr& p, const switchPattern<T>& f) { | 234 | 131 | switch (p->case_id()) { | 235 | 14 | case MatchLiteral::type_case_id: | 236 | 14 | return f.with(rcast<const MatchLiteral*>(p.get())); | 237 | 0 | case MatchAny::type_case_id: | 238 | 0 | return f.with(rcast<const MatchAny*>(p.get())); | 239 | 4 | case MatchArray::type_case_id: | 240 | 4 | return f.with(rcast<const MatchArray*>(p.get())); | 241 | 1 | case MatchRegex::type_case_id: | 242 | 1 | return f.with(rcast<const MatchRegex*>(p.get())); | 243 | 36 | case MatchRecord::type_case_id: | 244 | 36 | return f.with(rcast<const MatchRecord*>(p.get())); | 245 | 76 | case MatchVariant::type_case_id: | 246 | 76 | return f.with(rcast<const MatchVariant*>(p.get())); | 247 | 0 | default: | 248 | 0 | throw annotated_error(*p, "Internal error, cannot switch on unknown high-level pattern: " + show(p)); | 249 | 131 | } | 250 | 131 | } |
unsigned long hobbes::switchOf<unsigned long>(std::shared_ptr<hobbes::Pattern> const&, hobbes::switchPattern<unsigned long> const&) Line | Count | Source | 233 | 652 | T switchOf(const PatternPtr& p, const switchPattern<T>& f) { | 234 | 652 | switch (p->case_id()) { | 235 | 38 | case MatchLiteral::type_case_id: | 236 | 38 | return f.with(rcast<const MatchLiteral*>(p.get())); | 237 | 395 | case MatchAny::type_case_id: | 238 | 395 | return f.with(rcast<const MatchAny*>(p.get())); | 239 | 8 | case MatchArray::type_case_id: | 240 | 8 | return f.with(rcast<const MatchArray*>(p.get())); | 241 | 2 | case MatchRegex::type_case_id: | 242 | 2 | return f.with(rcast<const MatchRegex*>(p.get())); | 243 | 81 | case MatchRecord::type_case_id: | 244 | 81 | return f.with(rcast<const MatchRecord*>(p.get())); | 245 | 128 | case MatchVariant::type_case_id: | 246 | 128 | return f.with(rcast<const MatchVariant*>(p.get())); | 247 | 0 | default: | 248 | 0 | throw annotated_error(*p, "Internal error, cannot switch on unknown high-level pattern: " + show(p)); | 249 | 652 | } | 250 | 652 | } |
|
251 | | |
252 | | template <typename T> |
253 | | std::vector<T> switchOf(const Patterns& ps, const switchPattern<T>& f) { |
254 | | std::vector<T> r; |
255 | | for (auto p : ps) { |
256 | | r.push_back(switchOf(p, f)); |
257 | | } |
258 | | return r; |
259 | | } |
260 | | |
261 | | // construction utilities |
262 | 0 | inline MatchArray* mkpatarray(const std::vector<unsigned char>& bs, const LexicalAnnotation& la) { |
263 | 0 | Patterns ps; |
264 | 0 | for (auto b : bs) { |
265 | 0 | ps.push_back(PatternPtr(new MatchLiteral(PrimitivePtr(new Byte(b, la)), la))); |
266 | 0 | } |
267 | 0 | return new MatchArray(ps, la); |
268 | 0 | } |
269 | | |
270 | 4 | inline MatchArray* mkpatarray(const std::string& cs, const LexicalAnnotation& la) { |
271 | 4 | Patterns ps; |
272 | 11 | for (auto c : cs) { |
273 | 11 | ps.push_back(PatternPtr(new MatchLiteral(PrimitivePtr(new Char(c, la)), la))); |
274 | 11 | } |
275 | 4 | return new MatchArray(ps, la); |
276 | 4 | } |
277 | | |
278 | | // is it possible for a pattern to be refuted? |
279 | | bool refutable(const PatternPtr&); |
280 | | |
281 | | // is a pattern a match on unit? |
282 | | bool isUnitPat(const PatternPtr&); |
283 | | |
284 | | // what variable names are introduced by a pattern (excluding _)? |
285 | | str::set accessibleBindingNames(const PatternPtr&); |
286 | | |
287 | | // simplify introducing list comprehensions |
288 | | struct CSelection { |
289 | | PatternPtr pat; |
290 | | ExprPtr seq; |
291 | | Exprs conds; |
292 | | }; |
293 | | using CSelectionPtr = std::shared_ptr<CSelection>; |
294 | | using CSelections = std::vector<CSelectionPtr>; |
295 | | |
296 | | Expr* desugarComprehension(cc* c, const ExprPtr& ex, const CSelections& cs, const LexicalAnnotation& la); |
297 | | |
298 | | } |
299 | | |
300 | | // make sure that pattern rows can be used as keys in hash maps |
301 | | namespace std { |
302 | | template <> |
303 | | struct hash< hobbes::PatternRow > { |
304 | 2.35k | inline size_t operator()(const hobbes::PatternRow& x) const { |
305 | 2.35k | return hobbes::hash(x); |
306 | 2.35k | } |
307 | | }; |
308 | | } |
309 | | |
310 | | #endif |