22 #ifndef FST_EXTENSIONS_NGRAM_NGRAM_FST_H_ 23 #define FST_EXTENSIONS_NGRAM_NGRAM_FST_H_ 53 typedef typename A::Label
Label;
88 typedef typename A::Label
Label;
94 SetInputSymbols(
nullptr);
95 SetOutputSymbols(
nullptr);
96 SetProperties(kStaticProperties);
104 FSTERROR() <<
"Copying NGramFst Impls is not supported, use safe = false.";
109 auto impl = std::make_unique<NGramFstImpl<A>>();
111 if (!impl->ReadHeader(strm, opts, kMinFileVersion, &hdr))
return nullptr;
112 uint64_t num_states, num_futures, num_final;
113 const size_t offset =
114 sizeof(num_states) +
sizeof(num_futures) +
sizeof(num_final);
116 strm.read(reinterpret_cast<char *>(&num_states),
sizeof(num_states));
117 strm.read(reinterpret_cast<char *>(&num_futures),
sizeof(num_futures));
118 strm.read(reinterpret_cast<char *>(&num_final),
sizeof(num_final));
119 size_t size = Storage(num_states, num_futures, num_final);
121 char *data =
static_cast<char *
>(data_region->mutable_data());
123 memcpy(data, reinterpret_cast<char *>(&num_states),
sizeof(num_states));
124 memcpy(data +
sizeof(num_states), reinterpret_cast<char *>(&num_futures),
125 sizeof(num_futures));
126 memcpy(data +
sizeof(num_states) +
sizeof(num_futures),
127 reinterpret_cast<char *>(&num_final),
sizeof(num_final));
128 strm.read(data + offset, size - offset);
129 if (strm.fail())
return nullptr;
130 impl->Init(data, std::move(data_region));
131 return impl.release();
138 WriteHeader(strm, opts, kFileVersion, &hdr);
139 strm.write(data_, StorageSize());
143 StateId
Start()
const {
return start_; }
146 if (final_index_.Get(state)) {
147 return final_probs_[final_index_.Rank1(state)];
149 return Weight::Zero();
154 if (inst ==
nullptr) {
155 const std::pair<size_t, size_t> zeros =
156 (state == 0) ? select_root_ : future_index_.Select0s(state);
157 return zeros.second - zeros.first - 1;
159 SetInstFuture(state, inst);
160 return inst->num_futures_ + ((state == 0) ? 0 : 1);
165 if (state == 0)
return 0;
176 data->
base =
nullptr;
180 static size_t Storage(uint64_t num_states, uint64_t num_futures,
181 uint64_t num_final) {
186 sizeof(num_states) +
sizeof(num_futures) +
sizeof(num_final);
191 offset += (num_states + 1) *
sizeof(label) + num_futures *
sizeof(label);
194 offset = (offset +
sizeof(weight) - 1) & ~(
sizeof(weight) - 1);
195 offset += (num_states + 1) *
sizeof(weight) + num_final *
sizeof(weight) +
196 (num_futures + 1) *
sizeof(weight);
201 if (inst->
state_ != state) {
203 const std::pair<size_t, size_t> zeros = future_index_.Select0s(state);
205 inst->
offset_ = future_index_.Rank1(zeros.first + 1);
212 inst->
node_ = context_index_.Select1(inst->
state_);
221 size_t node = inst->
node_;
223 inst->
context_.push_back(context_words_[context_index_.Rank1(node)]);
224 node = context_index_.Select1(context_index_.Rank0(node) - 1);
230 const char *
GetData(
size_t *data_size)
const {
231 *data_size = StorageSize();
235 void Init(
const char *data,
236 std::unique_ptr<MappedFile> data_region);
239 SetInstFuture(s, inst);
240 SetInstContext(inst);
248 void GetStates(
const std::vector<Label> &context,
249 std::vector<StateId> *states)
const;
252 StateId Transition(
const std::vector<Label> &context, Label future)
const;
255 static constexpr uint64_t kStaticProperties =
261 static constexpr
int kFileVersion = 4;
263 static constexpr
int kMinFileVersion = 4;
265 std::unique_ptr<MappedFile> data_region_;
266 const char *data_ =
nullptr;
268 uint64_t num_states_ = 0;
270 uint64_t num_final_ = 0;
271 std::pair<size_t, size_t> select_root_;
272 const Label *root_children_ =
nullptr;
275 const uint64_t *future_ =
nullptr;
276 const uint64_t *final_ =
nullptr;
277 const Label *context_words_ =
nullptr;
278 const Label *future_words_ =
nullptr;
279 const Weight *backoff_ =
nullptr;
280 const Weight *final_probs_ =
nullptr;
281 const Weight *future_probs_ =
nullptr;
291 template <
typename A>
293 const std::vector<Label> &context,
294 std::vector<typename A::StateId> *states)
const {
296 states->push_back(0);
297 typename std::vector<Label>::const_reverse_iterator cit = context.rbegin();
298 const Label *children = root_children_;
299 size_t num_children = select_root_.second - 2;
300 const Label *loc = std::lower_bound(children, children + num_children, *cit);
301 if (loc == children + num_children || *loc != *cit)
return;
302 size_t node = 2 + loc - children;
303 states->push_back(context_index_.Rank1(node));
304 if (context.size() == 1)
return;
305 size_t node_rank = context_index_.Rank1(node);
306 std::pair<size_t, size_t> zeros =
307 node_rank == 0 ? select_root_ : context_index_.Select0s(node_rank);
308 size_t first_child = zeros.first + 1;
310 if (context_index_.Get(first_child) !=
false) {
311 size_t last_child = zeros.second - 1;
312 while (cit != context.rend()) {
313 children = context_words_ + context_index_.Rank1(first_child);
314 loc = std::lower_bound(children, children + last_child - first_child + 1,
316 if (loc == children + last_child - first_child + 1 || *loc != *cit) {
320 node = first_child + loc - children;
321 states->push_back(context_index_.Rank1(node));
322 node_rank = context_index_.Rank1(node);
324 node_rank == 0 ? select_root_ : context_index_.Select0s(node_rank);
325 first_child = zeros.first + 1;
326 if (context_index_.Get(first_child) ==
false)
break;
327 last_child = zeros.second - 1;
364 GetMutableImpl()->Init(data,
nullptr);
368 const char *
GetData(
size_t *data_size)
const {
369 return GetImpl()->GetData(data_size);
373 return GetImpl()->GetContext(s, &inst_);
379 std::vector<StateId> *state)
const {
380 return GetImpl()->GetStates(context, state);
384 return GetImpl()->NumArcs(s, &inst_);
392 Impl *impl = Impl::Read(strm, opts);
393 return impl ?
new NGramFst<A>(std::shared_ptr<Impl>(impl)) :
nullptr;
397 if (!source.empty()) {
398 std::ifstream strm(source,
399 std::ios_base::in | std::ios_base::binary);
401 LOG(ERROR) <<
"NGramFst::Read: Can't open file: " << source;
411 return GetImpl()->Write(strm, opts);
414 bool Write(
const std::string &source)
const override {
419 GetImpl()->InitStateIterator(data);
422 inline void InitArcIterator(StateId s,
432 static const auto props =
438 if (!HasRequiredProps(fst)) {
441 typename A::StateId unigram = fst.
Start();
445 if (aiter.
Done() || aiter.
Value().ilabel != 0)
break;
446 unigram = aiter.
Value().nextstate;
452 const typename A::StateId &state = siter.Value();
454 if (state != unigram) {
455 if (aiter.
Done())
return false;
456 if (aiter.
Value().ilabel != 0)
return false;
458 if (!aiter.
Done() && aiter.
Value().ilabel == 0)
return false;
468 explicit NGramFst(std::shared_ptr<Impl> impl)
477 GetImpl()->SetInstFuture(s, &inst_);
478 GetImpl()->SetInstNode(&inst_);
479 data->
base = std::make_unique<ArcIterator<NGramFst<A>>>(*
this, s);
484 template <
typename A>
486 std::vector<StateId> *order_out) {
488 typedef typename Arc::Label
Label;
489 typedef typename Arc::Weight
Weight;
490 typedef typename Arc::StateId
StateId;
494 SetProperties(kStaticProperties);
498 FSTERROR() <<
"NGramFst only accepts OpenGrm language models as input";
504 std::vector<Label> context(num_states, 0);
508 StateId unigram = fst.
Start();
511 FSTERROR() <<
"Could not identify unigram state";
517 LOG(WARNING) <<
"Unigram state " << unigram <<
" has no arcs.";
520 if (aiter.
Value().ilabel != 0)
break;
521 unigram = aiter.
Value().nextstate;
526 std::queue<std::pair<StateId, Label>> label_queue;
527 std::vector<bool> visited(num_states);
529 label_queue.push(std::make_pair(fst.
Start(), 0));
532 std::make_pair(aiter.Value().nextstate, aiter.Value().ilabel));
535 while (!label_queue.empty()) {
536 std::pair<StateId, Label> &now = label_queue.front();
537 if (!visited[now.first]) {
538 context[now.first] = now.second;
539 visited[now.first] =
true;
542 const Arc &arc = aiter.Value();
543 if (arc.ilabel != 0) {
544 label_queue.push(std::make_pair(arc.nextstate, now.second));
555 context[fst.
Start()] = 0;
559 uint64_t num_final = 0;
560 for (
int i = 0; i < num_states; ++i) {
561 if (fst.
Final(i) != Weight::Zero()) {
569 int64_t num_context_arcs = 0;
570 int64_t num_futures = 0;
572 const StateId &state = siter.Value();
576 const Arc &arc = aiter.
Value();
579 if (arc.ilabel == 0) {
580 context_fst.
AddArc(arc.nextstate, Arc(context[state], context[state],
586 if (num_context_arcs != context_fst.
NumStates() - 1) {
587 FSTERROR() <<
"Number of contexts arcs != number of states - 1";
591 if (context_fst.
NumStates() != num_states) {
592 FSTERROR() <<
"Number of contexts != number of states";
596 int64_t context_props =
599 FSTERROR() <<
"Input Fst is not structured properly";
610 const size_t storage = Storage(num_states, num_futures, num_final);
612 char *data =
static_cast<char *
>(data_region->mutable_data());
613 memset(data, 0, storage);
615 memcpy(data + offset, reinterpret_cast<char *>(&num_states),
617 offset +=
sizeof(num_states);
618 memcpy(data + offset, reinterpret_cast<char *>(&num_futures),
619 sizeof(num_futures));
620 offset +=
sizeof(num_futures);
621 memcpy(data + offset, reinterpret_cast<char *>(&num_final),
623 offset +=
sizeof(num_final);
624 uint64_t *context_bits =
reinterpret_cast<uint64_t *
>(data + offset);
626 uint64_t *future_bits =
reinterpret_cast<uint64_t *
>(data + offset);
629 uint64_t *final_bits =
reinterpret_cast<uint64_t *
>(data + offset);
631 Label *context_words =
reinterpret_cast<Label *
>(data + offset);
632 offset += (num_states + 1) *
sizeof(label);
633 Label *future_words =
reinterpret_cast<Label *
>(data + offset);
634 offset += num_futures *
sizeof(label);
635 offset = (offset +
sizeof(weight) - 1) & ~(
sizeof(weight) - 1);
636 Weight *backoff =
reinterpret_cast<Weight *
>(data + offset);
637 offset += (num_states + 1) *
sizeof(weight);
638 Weight *final_probs =
reinterpret_cast<Weight *
>(data + offset);
639 offset += num_final *
sizeof(weight);
640 Weight *future_probs =
reinterpret_cast<Weight *
>(data + offset);
641 int64_t context_arc = 0, future_arc = 0, context_bit = 0, future_bit = 0,
647 context_words[context_arc] = label;
648 backoff[context_arc] = Weight::Zero();
654 order_out->resize(num_states);
657 std::queue<StateId> context_q;
658 context_q.push(context_fst.
Start());
659 StateId state_number = 0;
660 while (!context_q.empty()) {
661 const StateId &state = context_q.front();
663 (*order_out)[state] = state_number;
666 const Weight final_weight = context_fst.
Final(state);
667 if (final_weight != Weight::Zero()) {
669 final_probs[final_bit] = final_weight;
675 const Arc &arc = aiter.Value();
676 context_words[context_arc] = arc.ilabel;
677 backoff[context_arc] = arc.weight;
680 context_q.push(arc.nextstate);
685 const Arc &arc = aiter.Value();
686 if (arc.ilabel != 0) {
687 future_words[future_arc] = arc.ilabel;
688 future_probs[future_arc] = arc.weight;
698 if ((state_number != num_states) || (context_bit != num_states * 2 + 1) ||
699 (context_arc != num_states) || (future_arc != num_futures) ||
700 (future_bit != num_futures + num_states + 1) ||
701 (final_bit != num_final)) {
702 FSTERROR() <<
"Structure problems detected during construction";
707 Init(data, std::move(data_region));
710 template <
typename A>
712 std::unique_ptr<MappedFile> data_region) {
713 data_region_ = std::move(data_region);
716 num_states_ = *(
reinterpret_cast<const uint64_t *
>(data_ + offset));
717 offset +=
sizeof(num_states_);
718 num_futures_ = *(
reinterpret_cast<const uint64_t *
>(data_ + offset));
720 num_final_ = *(
reinterpret_cast<const uint64_t *
>(data_ + offset));
721 offset +=
sizeof(num_final_);
723 size_t context_bits = num_states_ * 2 + 1;
725 context_ =
reinterpret_cast<const uint64_t *
>(data_ + offset);
727 future_ =
reinterpret_cast<const uint64_t *
>(data_ + offset);
729 final_ =
reinterpret_cast<const uint64_t *
>(data_ + offset);
731 context_words_ =
reinterpret_cast<const Label *
>(data_ + offset);
732 offset += (num_states_ + 1) *
sizeof(*context_words_);
733 future_words_ =
reinterpret_cast<const Label *
>(data_ + offset);
735 offset = (offset +
sizeof(*backoff_) - 1) & ~(
sizeof(*backoff_) - 1);
736 backoff_ =
reinterpret_cast<const Weight *
>(data_ + offset);
737 offset += (num_states_ + 1) *
sizeof(*backoff_);
738 final_probs_ =
reinterpret_cast<const Weight *
>(data_ + offset);
739 offset += num_final_ *
sizeof(*final_probs_);
740 future_probs_ =
reinterpret_cast<const Weight *
>(data_ + offset);
742 context_index_.BuildIndex(
context_, context_bits,
745 future_index_.BuildIndex(future_, future_bits,
748 final_index_.BuildIndex(final_, num_states_);
750 select_root_ = context_index_.Select0s(0);
751 if (context_index_.Rank1(0) != 0 || select_root_.first != 1 ||
752 context_index_.Get(2) ==
false) {
757 root_children_ = context_words_ + context_index_.Rank1(2);
761 template <
typename A>
763 const std::vector<Label> &context,
Label future)
const {
764 const Label *children = root_children_;
765 size_t num_children = select_root_.second - 2;
767 std::lower_bound(children, children + num_children, future);
768 if (loc == children + num_children || *loc != future) {
769 return context_index_.Rank1(0);
771 size_t node = 2 + loc - children;
772 size_t node_rank = context_index_.Rank1(node);
773 std::pair<size_t, size_t> zeros =
774 (node_rank == 0) ? select_root_ : context_index_.Select0s(node_rank);
775 size_t first_child = zeros.first + 1;
776 if (context_index_.Get(first_child) ==
false) {
777 return context_index_.Rank1(node);
779 size_t last_child = zeros.second - 1;
780 for (
int word = context.size() - 1; word >= 0; --word) {
781 children = context_words_ + context_index_.Rank1(first_child);
782 loc = std::lower_bound(children, children + last_child - first_child + 1,
784 if (loc == children + last_child - first_child + 1 ||
785 *loc != context[word]) {
788 node = first_child + loc - children;
789 node_rank = context_index_.Rank1(node);
791 (node_rank == 0) ? select_root_ : context_index_.Select0s(node_rank);
792 first_child = zeros.first + 1;
793 if (context_index_.Get(first_child) ==
false)
break;
794 last_child = zeros.second - 1;
796 return context_index_.Rank1(node);
812 : owned_fst_(fst.Copy()),
815 match_type_(match_type),
816 current_loop_(false),
819 std::swap(loop_.ilabel, loop_.olabel);
827 match_type_(match_type),
828 current_loop_(false),
831 std::swap(loop_.ilabel, loop_.olabel);
837 : owned_fst_(matcher.fst_.Copy(safe)),
839 inst_(matcher.inst_),
840 match_type_(matcher.match_type_),
841 current_loop_(false),
844 std::swap(loop_.ilabel, loop_.olabel);
856 uint64_t
Properties(uint64_t props)
const override {
return props; }
859 fst_.GetImpl()->SetInstFuture(s, &inst_);
860 current_loop_ =
false;
866 if (label == 0 || label == nolabel) {
868 current_loop_ =
true;
869 loop_.nextstate = inst_.state_;
872 if (inst_.state_ != 0) {
873 arc_.ilabel = arc_.olabel = 0;
874 fst_.GetImpl()->SetInstNode(&inst_);
875 arc_.nextstate = fst_.GetImpl()->context_index_.Rank1(
876 fst_.GetImpl()->context_index_.Select1(
877 fst_.GetImpl()->context_index_.Rank0(inst_.node_) - 1));
878 arc_.weight = fst_.GetImpl()->backoff_[inst_.state_];
882 current_loop_ =
false;
883 const Label *start = fst_.GetImpl()->future_words_ + inst_.offset_;
884 const Label *end = start + inst_.num_futures_;
885 const Label *search = std::lower_bound(start, end, label);
886 if (search != end && *search == label) {
887 size_t state = search - start;
888 arc_.ilabel = arc_.olabel = label;
889 arc_.weight = fst_.GetImpl()->future_probs_[inst_.offset_ + state];
890 fst_.GetImpl()->SetInstContext(&inst_);
891 arc_.nextstate = fst_.GetImpl()->Transition(inst_.context_, label);
898 bool Done() const final {
return !current_loop_ && done_; }
900 const Arc &
Value() const final {
return (current_loop_) ? loop_ : arc_; }
904 current_loop_ =
false;
910 ssize_t
Priority(StateId s)
final {
return fst_.NumArcs(s); }
913 std::unique_ptr<NGramFst<A>> owned_fst_;
933 : s_(0), num_states_(fst.NumStates()) {}
935 bool Done() const final {
return s_ >= num_states_; }
937 StateId
Value() const final {
return s_; }
958 : lazy_(~0), impl_(fst.GetImpl()), i_(0), flags_(
kArcValueFlags) {
960 impl_->SetInstFuture(state, &inst_);
961 impl_->SetInstNode(&inst_);
966 ((inst_.node_ == 0) ? inst_.num_futures_ : inst_.num_futures_ + 1);
970 bool eps = (inst_.node_ != 0 && i_ == 0);
971 StateId state = (inst_.node_ == 0) ? i_ : i_ - 1;
973 arc_.ilabel = arc_.olabel =
974 eps ? 0 : impl_->future_words_[inst_.offset_ + state];
980 impl_->context_index_.Rank1(impl_->context_index_.Select1(
981 impl_->context_index_.Rank0(inst_.node_) - 1));
983 if (lazy_ & kArcNextStateValue) {
984 impl_->SetInstContext(&inst_);
986 arc_.nextstate = impl_->Transition(
987 inst_.context_, impl_->future_words_[inst_.offset_ + state]);
989 lazy_ &= ~kArcNextStateValue;
992 arc_.weight = eps ? impl_->backoff_[inst_.state_]
993 : impl_->future_probs_[inst_.offset_ + state];
994 lazy_ &= ~kArcWeightValue;
1018 uint8_t
Flags() const final {
return flags_; }
1027 mutable uint8_t lazy_;
1036 #endif // FST_EXTENSIONS_NGRAM_NGRAM_FST_H_ constexpr uint64_t kCyclic
internal::NGramFstImpl< A > Impl
void GetStates(const std::vector< Label > &context, std::vector< StateId > *state) const
constexpr uint64_t kNotString
void AddArc(StateId s, const Arc &arc) override
NGramFst< A > * Copy(bool safe=false) const override
void SetStart(StateId s) override
constexpr uint8_t kArcValueFlags
size_t NumInputEpsilons(StateId state) const
virtual uint64_t Properties(uint64_t mask, bool test) const =0
constexpr uint64_t kOEpsilons
const Arc & Value() const final
void SetFinal(StateId s, Weight weight=Weight::One()) override
virtual size_t NumArcs(StateId) const =0
static size_t Storage(uint64_t num_states, uint64_t num_futures, uint64_t num_final)
constexpr uint64_t kCoAccessible
void SetInstContext(NGramFstInst< A > *inst) const
uint8_t Flags() const final
static void Set(uint64_t *bits, size_t index)
constexpr uint64_t kNotTopSorted
size_t Position() const final
constexpr uint64_t kError
size_t StorageSize() const
constexpr uint64_t kInitialAcyclic
static NGramFstImpl< A > * Read(std::istream &strm, const FstReadOptions &opts)
StateId Start() const override
virtual Weight Final(StateId) const =0
uint64_t Properties(uint64_t props) const override
constexpr uint8_t kArcILabelValue
constexpr uint64_t kEpsilons
constexpr uint64_t kODeterministic
const Arc & Value() const final
NGramFstMatcher< A > * Copy(bool safe=false) const override
const Arc & Value() const
NGramFstMatcher(const NGramFstMatcher< A > &matcher, bool safe=false)
static MappedFile * Allocate(size_t size, size_t align=kArchAlignment)
void InitArcIterator(StateId s, ArcIteratorData< A > *data) const override
NGramFst(const Fst< A > &fst, std::vector< StateId > *order_out)
const Fst< A > & GetFst() const override
virtual size_t NumInputEpsilons(StateId) const =0
StateId AddState() override
NGramFst(const Fst< A > &dst)
size_t NumArcs(StateId state, NGramFstInst< A > *inst=nullptr) const
const std::vector< Label > & GetContext(StateId s, NGramFstInst< A > *inst) const
void SetOutputSymbols(const SymbolTable *osyms) override
static size_t StorageSize(size_t num_bits)
NGramFst(const NGramFst< A > &fst, bool safe=false)
ssize_t NumInputEpsilons(const ExpandedFst< Arc > &fst, typename Arc::StateId s)
const char * GetData(size_t *data_size) const
void ArcSort(MutableFst< Arc > *fst, Compare comp)
constexpr uint64_t kOLabelSorted
void SetInstNode(NGramFstInst< A > *inst) const
void SetFlags(uint8_t flags, uint8_t mask) final
static NGramFst< A > * Read(std::istream &strm, const FstReadOptions &opts)
void SetInputSymbols(const SymbolTable *isyms) override
std::vector< Label > context_
MatcherBase< A > * InitMatcher(MatchType match_type) const override
bool Write(const std::string &source) const override
StateId Value() const final
StateId NumStates() const override
NGramFstMatcher(const NGramFst< A > &fst, MatchType match_type)
constexpr uint64_t kAccessible
constexpr uint8_t kArcOLabelValue
const char * GetData(size_t *data_size) const
MatchType Type(bool test) const override
size_t NumArcs(StateId s) const override
void SetInstFuture(StateId state, NGramFstInst< A > *inst) const
virtual StateId Start() const =0
std::unique_ptr< StateIteratorBase< Arc > > base
constexpr uint64_t kIDeterministic
void SetState(StateId s) final
NGramFstImpl(const Fst< A > &fst)
std::unique_ptr< ArcIteratorBase< Arc > > base
const std::vector< Label > GetContext(StateId s) const
constexpr uint64_t kIEpsilons
void Seek(size_t a) final
bool WriteFile(const std::string &source) const
StateId NumStates() const
constexpr uint8_t kArcWeightValue
size_t StorageSize() const
constexpr uint64_t kILabelSorted
bool Write(std::ostream &strm, const FstWriteOptions &opts) const override
virtual const SymbolTable * InputSymbols() const =0
Arc::StateId CountStates(const Fst< Arc > &fst)
constexpr uint8_t kArcNextStateValue
ssize_t Priority(StateId s) final
ArcIterator(const NGramFst< A > &fst, StateId state)
Weight Final(StateId s) const override
size_t NumOutputEpsilons(StateId state) const
NGramFstImpl(const NGramFstImpl &other)
NGramFstMatcher(const NGramFst< A > *fst, MatchType match_type)
NGramFst(const char *data)
void InitStateIterator(StateIteratorData< A > *data) const override
Weight Final(StateId state) const
void InitStateIterator(StateIteratorData< A > *data) const
bool Write(std::ostream &strm, const FstWriteOptions &opts) const
constexpr uint64_t kWeighted
bool Find(Label label) final
constexpr uint64_t kExpanded
static bool HasRequiredStructure(const Fst< A > &fst)
StateIterator(const NGramFst< A > &fst)
static NGramFst< A > * Read(const std::string &source)
static bool HasRequiredProps(const Fst< A > &fst)
uint64_t Properties(uint64_t mask, bool test) const override
constexpr uint64_t kAcceptor
virtual const SymbolTable * OutputSymbols() const =0