FST  openfst-1.7.2
OpenFst Library
compose.h
Go to the documentation of this file.
1 // See www.openfst.org for extensive documentation on this weighted
2 // finite-state transducer library.
3 //
4 // Composes a PDT and an FST.
5 
6 #ifndef FST_EXTENSIONS_PDT_COMPOSE_H_
7 #define FST_EXTENSIONS_PDT_COMPOSE_H_
8 
9 #include <list>
10 
11 #include <fst/extensions/pdt/pdt.h>
12 #include <fst/compose.h>
13 
14 namespace fst {
15 
16 // Returns paren arcs for Find(kNoLabel).
17 constexpr uint32 kParenList = 0x00000001;
18 
19 // Returns a kNolabel loop for Find(paren).
20 constexpr uint32 kParenLoop = 0x00000002;
21 
22 // This class is a matcher that treats parens as multi-epsilon labels.
23 // It is most efficient if the parens are in a range non-overlapping with
24 // the non-paren labels.
25 template <class F>
26 class ParenMatcher {
27  public:
28  using FST = F;
30  using Arc = typename FST::Arc;
31  using Label = typename Arc::Label;
32  using StateId = typename Arc::StateId;
33  using Weight = typename Arc::Weight;
34 
35  // This makes a copy of the FST.
36  ParenMatcher(const FST &fst, MatchType match_type,
37  uint32 flags = (kParenLoop | kParenList))
38  : matcher_(fst, match_type), match_type_(match_type), flags_(flags) {
39  if (match_type == MATCH_INPUT) {
40  loop_.ilabel = kNoLabel;
41  loop_.olabel = 0;
42  } else {
43  loop_.ilabel = 0;
44  loop_.olabel = kNoLabel;
45  }
46  loop_.weight = Weight::One();
47  loop_.nextstate = kNoStateId;
48  }
49 
50  // This doesn't copy the FST.
51  ParenMatcher(const FST *fst, MatchType match_type,
52  uint32 flags = (kParenLoop | kParenList))
53  : matcher_(fst, match_type), match_type_(match_type), flags_(flags) {
54  if (match_type == MATCH_INPUT) {
55  loop_.ilabel = kNoLabel;
56  loop_.olabel = 0;
57  } else {
58  loop_.ilabel = 0;
59  loop_.olabel = kNoLabel;
60  }
61  loop_.weight = Weight::One();
62  loop_.nextstate = kNoStateId;
63  }
64 
65  // This makes a copy of the FST.
66  ParenMatcher(const ParenMatcher<FST> &matcher, bool safe = false)
67  : matcher_(matcher.matcher_, safe),
68  match_type_(matcher.match_type_),
69  flags_(matcher.flags_),
70  open_parens_(matcher.open_parens_),
71  close_parens_(matcher.close_parens_),
72  loop_(matcher.loop_) {
73  loop_.nextstate = kNoStateId;
74  }
75 
76  ParenMatcher<FST> *Copy(bool safe = false) const {
77  return new ParenMatcher<FST>(*this, safe);
78  }
79 
80  MatchType Type(bool test) const { return matcher_.Type(test); }
81 
82  void SetState(StateId s) {
83  matcher_.SetState(s);
84  loop_.nextstate = s;
85  }
86 
87  bool Find(Label match_label);
88 
89  bool Done() const { return done_; }
90 
91  const Arc &Value() const { return paren_loop_ ? loop_ : matcher_.Value(); }
92 
93  void Next();
94 
95  Weight Final(StateId s) { return matcher_.Final(s); }
96 
97  ssize_t Priority(StateId s) { return matcher_.Priority(s); }
98 
99  const FST &GetFst() const { return matcher_.GetFst(); }
100 
101  uint64 Properties(uint64 props) const { return matcher_.Properties(props); }
102 
103  uint32 Flags() const { return matcher_.Flags(); }
104 
105  void AddOpenParen(Label label) {
106  if (label == 0) {
107  FSTERROR() << "ParenMatcher: Bad open paren label: 0";
108  } else {
109  open_parens_.Insert(label);
110  }
111  }
112 
113  void AddCloseParen(Label label) {
114  if (label == 0) {
115  FSTERROR() << "ParenMatcher: Bad close paren label: 0";
116  } else {
117  close_parens_.Insert(label);
118  }
119  }
120 
121  void RemoveOpenParen(Label label) {
122  if (label == 0) {
123  FSTERROR() << "ParenMatcher: Bad open paren label: 0";
124  } else {
125  open_parens_.Erase(label);
126  }
127  }
128 
129  void RemoveCloseParen(Label label) {
130  if (label == 0) {
131  FSTERROR() << "ParenMatcher: Bad close paren label: 0";
132  } else {
133  close_parens_.Erase(label);
134  }
135  }
136 
137  void ClearOpenParens() { open_parens_.Clear(); }
138 
139  void ClearCloseParens() { close_parens_.Clear(); }
140 
141  bool IsOpenParen(Label label) const { return open_parens_.Member(label); }
142 
143  bool IsCloseParen(Label label) const { return close_parens_.Member(label); }
144 
145  private:
146  // Advances matcher to next open paren, returning true if it exists.
147  bool NextOpenParen();
148 
149  // Advances matcher to next close paren, returning true if it exists.
150  bool NextCloseParen();
151 
152  M matcher_;
153  MatchType match_type_; // Type of match to perform.
154  uint32 flags_;
155  // Open paren label set.
156  CompactSet<Label, kNoLabel> open_parens_;
157  // Close paren label set.
158  CompactSet<Label, kNoLabel> close_parens_;
159  bool open_paren_list_; // Matching open paren list?
160  bool close_paren_list_; // Matching close paren list?
161  bool paren_loop_; // Current arc is the implicit paren loop?
162  mutable Arc loop_; // For non-consuming symbols.
163  bool done_; // Matching done?
164 
165  ParenMatcher &operator=(const ParenMatcher &) = delete;
166 };
167 
168 template <class FST>
169 inline bool ParenMatcher<FST>::Find(Label match_label) {
170  open_paren_list_ = false;
171  close_paren_list_ = false;
172  paren_loop_ = false;
173  done_ = false;
174  // Returns all parenthesis arcs.
175  if (match_label == kNoLabel && (flags_ & kParenList)) {
176  if (open_parens_.LowerBound() != kNoLabel) {
177  matcher_.LowerBound(open_parens_.LowerBound());
178  open_paren_list_ = NextOpenParen();
179  if (open_paren_list_) return true;
180  }
181  if (close_parens_.LowerBound() != kNoLabel) {
182  matcher_.LowerBound(close_parens_.LowerBound());
183  close_paren_list_ = NextCloseParen();
184  if (close_paren_list_) return true;
185  }
186  }
187  // Returns the implicit paren loop.
188  if (match_label > 0 && (flags_ & kParenLoop) &&
189  (IsOpenParen(match_label) || IsCloseParen(match_label))) {
190  paren_loop_ = true;
191  return true;
192  }
193  // Returns all other labels.
194  if (matcher_.Find(match_label)) return true;
195  done_ = true;
196  return false;
197 }
198 
199 template <class FST>
200 inline void ParenMatcher<FST>::Next() {
201  if (paren_loop_) {
202  paren_loop_ = false;
203  done_ = true;
204  } else if (open_paren_list_) {
205  matcher_.Next();
206  open_paren_list_ = NextOpenParen();
207  if (open_paren_list_) return;
208  if (close_parens_.LowerBound() != kNoLabel) {
209  matcher_.LowerBound(close_parens_.LowerBound());
210  close_paren_list_ = NextCloseParen();
211  if (close_paren_list_) return;
212  }
213  done_ = !matcher_.Find(kNoLabel);
214  } else if (close_paren_list_) {
215  matcher_.Next();
216  close_paren_list_ = NextCloseParen();
217  if (close_paren_list_) return;
218  done_ = !matcher_.Find(kNoLabel);
219  } else {
220  matcher_.Next();
221  done_ = matcher_.Done();
222  }
223 }
224 
225 // Advances matcher to next open paren, returning true if it exists.
226 template <class FST>
227 inline bool ParenMatcher<FST>::NextOpenParen() {
228  for (; !matcher_.Done(); matcher_.Next()) {
229  Label label = match_type_ == MATCH_INPUT ? matcher_.Value().ilabel
230  : matcher_.Value().olabel;
231  if (label > open_parens_.UpperBound()) return false;
232  if (IsOpenParen(label)) return true;
233  }
234  return false;
235 }
236 
237 // Advances matcher to next close paren, returning true if it exists.
238 template <class FST>
239 inline bool ParenMatcher<FST>::NextCloseParen() {
240  for (; !matcher_.Done(); matcher_.Next()) {
241  Label label = match_type_ == MATCH_INPUT ? matcher_.Value().ilabel
242  : matcher_.Value().olabel;
243  if (label > close_parens_.UpperBound()) return false;
244  if (IsCloseParen(label)) return true;
245  }
246  return false;
247 }
248 
249 template <class Filter>
250 class ParenFilter {
251  public:
252  using FST1 = typename Filter::FST1;
253  using FST2 = typename Filter::FST2;
254  using Arc = typename Filter::Arc;
255  using Label = typename Arc::Label;
256  using StateId = typename Arc::StateId;
257  using Weight = typename Arc::Weight;
258 
259  using Matcher1 = typename Filter::Matcher1;
260  using Matcher2 = typename Filter::Matcher2;
261 
262  using StackId = StateId;
264  using FilterState1 = typename Filter::FilterState;
267 
268  ParenFilter(const FST1 &fst1, const FST2 &fst2, Matcher1 *matcher1 = nullptr,
269  Matcher2 *matcher2 = nullptr,
270  const std::vector<std::pair<Label, Label>> *parens = nullptr,
271  bool expand = false, bool keep_parens = true)
272  : filter_(fst1, fst2, matcher1, matcher2),
273  parens_(parens ? *parens : std::vector<std::pair<Label, Label>>()),
274  expand_(expand),
275  keep_parens_(keep_parens),
276  fs_(FilterState::NoState()),
277  stack_(parens_),
278  paren_id_(-1) {
279  if (parens) {
280  for (const auto &pair : *parens) {
281  parens_.push_back(pair);
282  GetMatcher1()->AddOpenParen(pair.first);
283  GetMatcher2()->AddOpenParen(pair.first);
284  if (!expand_) {
285  GetMatcher1()->AddCloseParen(pair.second);
286  GetMatcher2()->AddCloseParen(pair.second);
287  }
288  }
289  }
290  }
291 
292  ParenFilter(const ParenFilter &filter, bool safe = false)
293  : filter_(filter.filter_, safe),
294  parens_(filter.parens_),
295  expand_(filter.expand_),
296  keep_parens_(filter.keep_parens_),
297  fs_(FilterState::NoState()),
298  stack_(filter.parens_),
299  paren_id_(-1) {}
300 
301  FilterState Start() const {
302  return FilterState(filter_.Start(), FilterState2(0));
303  }
304 
305  void SetState(StateId s1, StateId s2, const FilterState &fs) {
306  fs_ = fs;
307  filter_.SetState(s1, s2, fs_.GetState1());
308  if (!expand_) return;
309  ssize_t paren_id = stack_.Top(fs.GetState2().GetState());
310  if (paren_id != paren_id_) {
311  if (paren_id_ != -1) {
312  GetMatcher1()->RemoveCloseParen(parens_[paren_id_].second);
313  GetMatcher2()->RemoveCloseParen(parens_[paren_id_].second);
314  }
315  paren_id_ = paren_id;
316  if (paren_id_ != -1) {
317  GetMatcher1()->AddCloseParen(parens_[paren_id_].second);
318  GetMatcher2()->AddCloseParen(parens_[paren_id_].second);
319  }
320  }
321  }
322 
323  FilterState FilterArc(Arc *arc1, Arc *arc2) const {
324  const auto fs1 = filter_.FilterArc(arc1, arc2);
325  const auto &fs2 = fs_.GetState2();
326  if (fs1 == FilterState1::NoState()) return FilterState::NoState();
327  if (arc1->olabel == kNoLabel && arc2->ilabel) { // arc2 parentheses.
328  if (keep_parens_) {
329  arc1->ilabel = arc2->ilabel;
330  } else if (arc2->ilabel) {
331  arc2->olabel = arc1->ilabel;
332  }
333  return FilterParen(arc2->ilabel, fs1, fs2);
334  } else if (arc2->ilabel == kNoLabel && arc1->olabel) { // arc1 parentheses.
335  if (keep_parens_) {
336  arc2->olabel = arc1->olabel;
337  } else {
338  arc1->ilabel = arc2->olabel;
339  }
340  return FilterParen(arc1->olabel, fs1, fs2);
341  } else {
342  return FilterState(fs1, fs2);
343  }
344  }
345 
346  void FilterFinal(Weight *w1, Weight *w2) const {
347  if (fs_.GetState2().GetState() != 0) *w1 = Weight::Zero();
348  filter_.FilterFinal(w1, w2);
349  }
350 
351  // Returns respective matchers; ownership stays with filter.
352 
353  Matcher1 *GetMatcher1() { return filter_.GetMatcher1(); }
354 
355  Matcher2 *GetMatcher2() { return filter_.GetMatcher2(); }
356 
357  uint64 Properties(uint64 iprops) const {
358  return filter_.Properties(iprops) & kILabelInvariantProperties &
360  }
361 
362  private:
363  const FilterState FilterParen(Label label, const FilterState1 &fs1,
364  const FilterState2 &fs2) const {
365  if (!expand_) return FilterState(fs1, fs2);
366  const auto stack_id = stack_.Find(fs2.GetState(), label);
367  if (stack_id < 0) {
368  return FilterState::NoState();
369  } else {
370  return FilterState(fs1, FilterState2(stack_id));
371  }
372  }
373 
374  Filter filter_;
375  std::vector<std::pair<Label, Label>> parens_;
376  bool expand_; // Expands to FST?
377  bool keep_parens_; // Retains parentheses in output?
378  FilterState fs_; // Current filter state.
379  mutable ParenStack stack_;
380  ssize_t paren_id_;
381 };
382 
383 // Class to setup composition options for PDT composition. Default is to take
384 // the PDT as the first composition argument.
385 template <class Arc, bool left_pdt = true>
387  : public ComposeFstOptions<
388  Arc, ParenMatcher<Fst<Arc>>,
389  ParenFilter<AltSequenceComposeFilter<ParenMatcher<Fst<Arc>>>>> {
390  public:
391  using Label = typename Arc::Label;
394 
398 
400  const std::vector<std::pair<Label, Label>> &parens,
401  const Fst<Arc> &ifst2, bool expand = false,
402  bool keep_parens = true) {
403  matcher1 = new PdtMatcher(ifst1, MATCH_OUTPUT, kParenList);
404  matcher2 = new PdtMatcher(ifst2, MATCH_INPUT, kParenLoop);
405  filter = new PdtFilter(ifst1, ifst2, matcher1, matcher2, &parens, expand,
406  keep_parens);
407  }
408 };
409 
410 // Class to setup composition options for PDT with FST composition.
411 // Specialization is for the FST as the first composition argument.
412 template <class Arc>
414  : public ComposeFstOptions<
415  Arc, ParenMatcher<Fst<Arc>>,
416  ParenFilter<SequenceComposeFilter<ParenMatcher<Fst<Arc>>>>> {
417  public:
418  using Label = typename Arc::Label;
421 
425 
426  PdtComposeFstOptions(const Fst<Arc> &ifst1, const Fst<Arc> &ifst2,
427  const std::vector<std::pair<Label, Label>> &parens,
428  bool expand = false, bool keep_parens = true) {
429  matcher1 = new PdtMatcher(ifst1, MATCH_OUTPUT, kParenLoop);
430  matcher2 = new PdtMatcher(ifst2, MATCH_INPUT, kParenList);
431  filter = new PdtFilter(ifst1, ifst2, matcher1, matcher2, &parens, expand,
432  keep_parens);
433  }
434 };
435 
437  PAREN_FILTER, // Bar-Hillel construction; keeps parentheses.
438  EXPAND_FILTER, // Bar-Hillel + expansion; removes parentheses.
439  EXPAND_PAREN_FILTER, // Bar-Hillel + expansion; keeps parentheses.
440 };
441 
443  bool connect; // Connect output?
444  PdtComposeFilter filter_type; // Pre-defined filter to use.
445 
446  explicit PdtComposeOptions(bool connect = true,
447  PdtComposeFilter filter_type = PAREN_FILTER)
448  : connect(connect), filter_type(filter_type) {}
449 };
450 
451 // Composes pushdown transducer (PDT) encoded as an FST (1st arg) and an FST
452 // (2nd arg) with the result also a PDT encoded as an FST (3rd arg). In the
453 // PDTs, some transitions are labeled with open or close parentheses. To be
454 // interpreted as a PDT, the parens must balance on a path (see PdtExpand()).
455 // The open-close parenthesis label pairs are passed using the parens argument.
456 template <class Arc>
457 void Compose(const Fst<Arc> &ifst1,
458  const std::vector<
459  std::pair<typename Arc::Label, typename Arc::Label>> &parens,
460  const Fst<Arc> &ifst2, MutableFst<Arc> *ofst,
461  const PdtComposeOptions &opts = PdtComposeOptions()) {
462  bool expand = opts.filter_type != PAREN_FILTER;
463  bool keep_parens = opts.filter_type != EXPAND_FILTER;
464  PdtComposeFstOptions<Arc, true> copts(ifst1, parens, ifst2, expand,
465  keep_parens);
466  copts.gc_limit = 0;
467  *ofst = ComposeFst<Arc>(ifst1, ifst2, copts);
468  if (opts.connect) Connect(ofst);
469 }
470 
471 // Composes an FST (1st arg) and pushdown transducer (PDT) encoded as an FST
472 // (2nd arg) with the result also a PDT encoded as an FST (3rd arg). In the
473 // PDTs, some transitions are labeled with open or close parentheses. To be
474 // interpreted as a PDT, the parens must balance on a path (see ExpandFst()).
475 // The open-close parenthesis label pairs are passed using the parens argument.
476 template <class Arc>
477 void Compose(const Fst<Arc> &ifst1, const Fst<Arc> &ifst2,
478  const std::vector<
479  std::pair<typename Arc::Label, typename Arc::Label>> &parens,
480  MutableFst<Arc> *ofst,
481  const PdtComposeOptions &opts = PdtComposeOptions()) {
482  bool expand = opts.filter_type != PAREN_FILTER;
483  bool keep_parens = opts.filter_type != EXPAND_FILTER;
484  PdtComposeFstOptions<Arc, false> copts(ifst1, ifst2, parens, expand,
485  keep_parens);
486  copts.gc_limit = 0;
487  *ofst = ComposeFst<Arc>(ifst1, ifst2, copts);
488  if (opts.connect) Connect(ofst);
489 }
490 
491 } // namespace fst
492 
493 #endif // FST_EXTENSIONS_PDT_COMPOSE_H_
void SetState(StateId s1, StateId s2, const FilterState &fs)
Definition: compose.h:305
void Next() final
Definition: matcher.h:295
void RemoveOpenParen(Label label)
Definition: compose.h:121
void SetState(StateId s) final
Definition: matcher.h:234
FilterState Start() const
Definition: compose.h:301
PdtComposeFstOptions(const Fst< Arc > &ifst1, const Fst< Arc > &ifst2, const std::vector< std::pair< Label, Label >> &parens, bool expand=false, bool keep_parens=true)
Definition: compose.h:426
bool IsOpenParen(Label label) const
Definition: compose.h:141
constexpr int kNoLabel
Definition: fst.h:179
typename FST::Arc Arc
Definition: compose.h:30
uint64_t uint64
Definition: types.h:32
Matcher1 * GetMatcher1()
Definition: compose.h:353
typename AltSequenceComposeFilter< ParenMatcher< Fst< Arc > > >::Arc Arc
Definition: compose.h:254
void LowerBound(Label label)
Definition: matcher.h:266
void RemoveCloseParen(Label label)
Definition: compose.h:129
const Arc & Value() const final
Definition: matcher.h:289
MatchType
Definition: fst.h:171
uint64 Properties(uint64 iprops) const
Definition: compose.h:357
Key LowerBound() const
Definition: util.h:414
const Arc & Value() const
Definition: compose.h:91
ParenMatcher(const FST *fst, MatchType match_type, uint32 flags=(kParenLoop|kParenList))
Definition: compose.h:51
PdtComposeFilter
Definition: compose.h:436
bool Done() const
Definition: compose.h:89
ParenMatcher(const FST &fst, MatchType match_type, uint32 flags=(kParenLoop|kParenList))
Definition: compose.h:36
uint64 Properties(uint64 inprops) const override
Definition: matcher.h:313
void Connect(MutableFst< Arc > *fst)
Definition: connect.h:268
void Erase(Key key)
Definition: util.h:375
Key UpperBound() const
Definition: util.h:417
MatchType Type(bool test) const override
Definition: matcher.h:218
FilterState FilterArc(Arc *arc1, Arc *arc2) const
Definition: compose.h:323
typename AltSequenceComposeFilter< ParenMatcher< Fst< Arc > > >::FST1 FST1
Definition: compose.h:252
constexpr int kNoStateId
Definition: fst.h:180
uint64 Properties(uint64 props) const
Definition: compose.h:101
PdtComposeFstOptions(const Fst< Arc > &ifst1, const std::vector< std::pair< Label, Label >> &parens, const Fst< Arc > &ifst2, bool expand=false, bool keep_parens=true)
Definition: compose.h:399
typename Arc::StateId StateId
Definition: compose.h:32
#define FSTERROR()
Definition: util.h:35
const FS2 & GetState2() const
Definition: filter-state.h:164
bool Done() const final
Definition: matcher.h:279
bool Find(Label match_label) final
Definition: matcher.h:248
MatchType Type(bool test) const
Definition: compose.h:80
void SetState(StateId s)
Definition: compose.h:82
void ClearOpenParens()
Definition: compose.h:137
void Compose(const Fst< Arc > &ifst1, const Fst< Arc > &ifst2, MutableFst< Arc > *ofst, const ComposeOptions &opts=ComposeOptions())
Definition: compose.h:981
Weight Final(StateId s) const final
Definition: matcher.h:303
ParenMatcher< FST > * Copy(bool safe=false) const
Definition: compose.h:76
bool IsCloseParen(Label label) const
Definition: compose.h:143
void Insert(Key key)
Definition: util.h:369
ParenFilter(const ParenFilter &filter, bool safe=false)
Definition: compose.h:292
void Clear()
Definition: util.h:386
void ClearCloseParens()
Definition: compose.h:139
void FilterFinal(Weight *w1, Weight *w2) const
Definition: compose.h:346
void SetState(const FS1 &fs1, const FS2 &fs2)
Definition: filter-state.h:166
void AddCloseParen(Label label)
Definition: compose.h:113
const FST & GetFst() const
Definition: compose.h:99
constexpr uint32 kParenList
Definition: compose.h:17
PdtComposeFilter filter_type
Definition: compose.h:444
virtual uint32 Flags() const
Definition: matcher.h:138
typename AltSequenceComposeFilter< ParenMatcher< Fst< Arc > > >::FST2 FST2
Definition: compose.h:253
ssize_t Priority(StateId s)
Definition: compose.h:97
constexpr uint64 kOLabelInvariantProperties
Definition: properties.h:245
Weight Final(StateId s)
Definition: compose.h:95
ssize_t Priority(StateId s) final
Definition: matcher.h:307
uint32_t uint32
Definition: types.h:31
Matcher2 * GetMatcher2()
Definition: compose.h:355
typename Arc::Weight Weight
Definition: compose.h:33
uint32 Flags() const
Definition: compose.h:103
bool Member(Key key) const
Definition: util.h:399
ParenFilter(const FST1 &fst1, const FST2 &fst2, Matcher1 *matcher1=nullptr, Matcher2 *matcher2=nullptr, const std::vector< std::pair< Label, Label >> *parens=nullptr, bool expand=false, bool keep_parens=true)
Definition: compose.h:268
const FST & GetFst() const override
Definition: matcher.h:311
PdtComposeOptions(bool connect=true, PdtComposeFilter filter_type=PAREN_FILTER)
Definition: compose.h:446
bool Find(Label match_label)
Definition: compose.h:169
ParenMatcher(const ParenMatcher< FST > &matcher, bool safe=false)
Definition: compose.h:66
constexpr uint32 kParenLoop
Definition: compose.h:20
typename AltSequenceComposeFilter< ParenMatcher< Fst< Arc > > >::FilterState FilterState1
Definition: compose.h:264
typename Arc::Label Label
Definition: compose.h:391
typename AltSequenceComposeFilter< ParenMatcher< Fst< Arc > > >::Matcher1 Matcher1
Definition: compose.h:259
typename Arc::Label Label
Definition: compose.h:31
constexpr uint64 kILabelInvariantProperties
Definition: properties.h:236
size_t gc_limit
Definition: cache.h:29
typename AltSequenceComposeFilter< ParenMatcher< Fst< Arc > > >::Matcher2 Matcher2
Definition: compose.h:260
void AddOpenParen(Label label)
Definition: compose.h:105