react-native-executorch 0.10.6 → 0.10.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/module/fetcher/telemetry.js +1 -1
- package/package.json +1 -1
- package/react-native-executorch.podspec +2 -0
- package/src/fetcher/telemetry.ts +1 -1
- package/third-party/common/phonemis/CMakeLists.txt +70 -14
- package/third-party/common/phonemis/README.md +183 -33
- package/third-party/common/phonemis/THIRD_PARTY_NOTICES.md +57 -0
- package/third-party/common/phonemis/knowledge/code_style.md +642 -0
- package/third-party/common/phonemis/licenses/Unicode-3.0.txt +39 -0
- package/third-party/common/phonemis/licenses/misaki-Apache-2.0.txt +201 -0
- package/third-party/common/phonemis/licenses/nlohmann-json-MIT.txt +21 -0
- package/third-party/common/phonemis/licenses/xsimd-BSD-3-Clause.txt +29 -0
- package/third-party/common/phonemis/src/phonemis/base/config.h +6 -0
- package/third-party/common/phonemis/src/phonemis/base/ipipeline.cpp +75 -0
- package/third-party/common/phonemis/src/phonemis/base/ipipeline.h +21 -0
- package/third-party/common/phonemis/src/phonemis/base/phonemizer/constants.h +2 -5
- package/third-party/common/phonemis/src/phonemis/base/phonemizer/lexicon_phonemizer.cpp +3 -4
- package/third-party/common/phonemis/src/phonemis/base/phonemizer/nn/neural_phonemizer.cpp +17 -8
- package/third-party/common/phonemis/src/phonemis/base/phonemizer/nn/neural_phonemizer.h +4 -0
- package/third-party/common/phonemis/src/phonemis/base/phonemizer/nn/tokenizer.cpp +21 -6
- package/third-party/common/phonemis/src/phonemis/base/phonemizer/nn/tokenizer.h +13 -4
- package/third-party/common/phonemis/src/phonemis/base/pipeline.cpp +8 -0
- package/third-party/common/phonemis/src/phonemis/base/pipeline.h +19 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/common/conditions.cpp +37 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/common/conditions.h +45 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/common/constants.h +10 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/common/context.cpp +105 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/common/context.h +49 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/common/document.cpp +173 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/common/document.h +67 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/common/text.cpp +75 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/common/text.h +60 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/common/types.h +18 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/config.h +53 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layer.h +9 -7
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/normalize/constants.h +20 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/normalize/layer.cpp +89 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/normalize/layer.h +35 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/normalize/types.h +16 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/kind.h +40 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/layer.h +382 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/notation.h +30 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/numerals.cpp +51 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/numerals.h +31 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/pattern.h +30 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/pattern_set.cpp +17 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/pattern_set.h +31 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/patterns.cpp +811 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/patterns.h +285 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/primitives.cpp +64 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/primitives.h +23 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/types.h +94 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/sanitizer/layer.cpp +19 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/{sanitizer_layer.h → layers/sanitizer/layer.h} +8 -8
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/sym2word/layer.cpp +65 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/sym2word/layer.h +29 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/term2word/layer.cpp +170 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/term2word/layer.h +40 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/term2word/types.h +25 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/trim/layer.cpp +47 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/layers/trim/layer.h +19 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/preprocessor.cpp +60 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/preprocessor.h +45 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/processor.cpp +6 -7
- package/third-party/common/phonemis/src/phonemis/base/processor/processor.h +14 -10
- package/third-party/common/phonemis/src/phonemis/base/tagger/hmm_tagger.cpp +89 -52
- package/third-party/common/phonemis/src/phonemis/base/tagger/hmm_tagger.h +16 -9
- package/third-party/common/phonemis/src/phonemis/base/tokenizer/tokenizer.cpp +13 -12
- package/third-party/common/phonemis/src/phonemis/lang/de/constants.h +183 -19
- package/third-party/common/phonemis/src/phonemis/lang/de/num2word.cpp +355 -309
- package/third-party/common/phonemis/src/phonemis/lang/de/num2word.h +42 -17
- package/third-party/common/phonemis/src/phonemis/lang/de/patterns.cpp +56 -0
- package/third-party/common/phonemis/src/phonemis/lang/de/patterns.h +15 -0
- package/third-party/common/phonemis/src/phonemis/lang/de/pipeline.h +3 -3
- package/third-party/common/phonemis/src/phonemis/lang/en/constants.h +181 -30
- package/third-party/common/phonemis/src/phonemis/lang/en/hmm_tagger.cpp +5 -7
- package/third-party/common/phonemis/src/phonemis/lang/en/num2word.cpp +232 -135
- package/third-party/common/phonemis/src/phonemis/lang/en/num2word.h +41 -16
- package/third-party/common/phonemis/src/phonemis/lang/en/pipeline.h +3 -10
- package/third-party/common/phonemis/src/phonemis/lang/es/constants.h +182 -60
- package/third-party/common/phonemis/src/phonemis/lang/es/num2word.cpp +247 -212
- package/third-party/common/phonemis/src/phonemis/lang/es/num2word.h +38 -18
- package/third-party/common/phonemis/src/phonemis/lang/es/patterns.cpp +50 -0
- package/third-party/common/phonemis/src/phonemis/lang/es/patterns.h +15 -0
- package/third-party/common/phonemis/src/phonemis/lang/es/pipeline.h +3 -3
- package/third-party/common/phonemis/src/phonemis/lang/fr/constants.h +152 -26
- package/third-party/common/phonemis/src/phonemis/lang/fr/num2word.cpp +252 -179
- package/third-party/common/phonemis/src/phonemis/lang/fr/num2word.h +38 -15
- package/third-party/common/phonemis/src/phonemis/lang/fr/patterns.cpp +83 -0
- package/third-party/common/phonemis/src/phonemis/lang/fr/patterns.h +15 -0
- package/third-party/common/phonemis/src/phonemis/lang/fr/pipeline.h +3 -3
- package/third-party/common/phonemis/src/phonemis/lang/hi/constants.h +62 -10
- package/third-party/common/phonemis/src/phonemis/lang/hi/num2word.cpp +74 -123
- package/third-party/common/phonemis/src/phonemis/lang/hi/num2word.h +27 -16
- package/third-party/common/phonemis/src/phonemis/lang/hi/pipeline.h +5 -3
- package/third-party/common/phonemis/src/phonemis/lang/it/constants.h +122 -42
- package/third-party/common/phonemis/src/phonemis/lang/it/num2word.cpp +200 -207
- package/third-party/common/phonemis/src/phonemis/lang/it/num2word.h +30 -18
- package/third-party/common/phonemis/src/phonemis/lang/it/patterns.cpp +25 -0
- package/third-party/common/phonemis/src/phonemis/lang/it/patterns.h +15 -0
- package/third-party/common/phonemis/src/phonemis/lang/it/pipeline.h +3 -3
- package/third-party/common/phonemis/src/phonemis/lang/pl/constants.h +346 -77
- package/third-party/common/phonemis/src/phonemis/lang/pl/num2word.cpp +631 -246
- package/third-party/common/phonemis/src/phonemis/lang/pl/num2word.h +47 -15
- package/third-party/common/phonemis/src/phonemis/lang/pl/pipeline.h +6 -6
- package/third-party/common/phonemis/src/phonemis/lang/pt/constants.h +201 -0
- package/third-party/common/phonemis/src/phonemis/lang/pt/num2word.cpp +365 -0
- package/third-party/common/phonemis/src/phonemis/lang/pt/num2word.h +53 -0
- package/third-party/common/phonemis/src/phonemis/lang/pt/pipeline.h +4 -2
- package/third-party/common/phonemis/src/phonemis/main.cpp +42 -11
- package/third-party/common/phonemis/src/phonemis/protophone/meta.h +24 -0
- package/third-party/common/phonemis/src/phonemis/protophone/numeric.h +26 -0
- package/third-party/common/phonemis/src/phonemis/protophone/protophone.cpp +525 -0
- package/third-party/common/phonemis/src/phonemis/protophone/protophone.h +80 -0
- package/third-party/common/phonemis/src/phonemis/protophone/tensor.h +76 -0
- package/third-party/common/phonemis/src/phonemis/protophone/tensor_utils.h +89 -0
- package/third-party/common/phonemis/src/phonemis/protophone/tensor_view.h +125 -0
- package/third-party/common/phonemis/src/phonemis/protophone/types.h +43 -0
- package/third-party/common/phonemis/src/phonemis/utils/strings.h +20 -14
- package/third-party/common/phonemis/src/phonemis/utils/unicode.h +15 -1
- package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_arithmetic.hpp +267 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_bit.hpp +232 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_cast.hpp +43 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_complex.hpp +112 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_details.hpp +386 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_logical.hpp +240 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_math.hpp +2601 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_memory.hpp +1006 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_rounding.hpp +72 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_swizzle.hpp +260 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_trigo.hpp +996 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/utils/shifts.hpp +82 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx.hpp +2111 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx2.hpp +1591 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx2_128.hpp +170 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512bw.hpp +724 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512cd.hpp +28 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512dq.hpp +270 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512er.hpp +17 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512f.hpp +2919 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512ifma.hpp +17 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512pf.hpp +17 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512vbmi.hpp +76 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512vbmi2.hpp +130 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512vnni_avx512bw.hpp +17 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512vnni_avx512vbmi2.hpp +17 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx_128.hpp +164 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avxvnni.hpp +17 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_common.hpp +26 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_common_fwd.hpp +127 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_constants.hpp +393 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_emulated.hpp +787 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_fma3_avx.hpp +93 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_fma3_avx2.hpp +46 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_fma3_sse.hpp +97 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_fma4.hpp +92 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_i8mm_neon64.hpp +17 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_isa.hpp +148 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_neon.hpp +3580 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_neon64.hpp +1614 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_rvv.hpp +1515 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_scalar.hpp +1210 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_sse2.hpp +2333 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_sse3.hpp +66 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_sse4_1.hpp +510 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_sse4_2.hpp +44 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_ssse3.hpp +186 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_sve.hpp +1186 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_vsx.hpp +947 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_vxe.hpp +787 -0
- package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_wasm.hpp +1819 -0
- package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_arch.hpp +242 -0
- package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_config.hpp +633 -0
- package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpu_features.hpp +50 -0
- package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpu_features_arm.hpp +132 -0
- package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpu_features_ppc.hpp +54 -0
- package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpu_features_riscv.hpp +95 -0
- package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpu_features_s390x.hpp +56 -0
- package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpu_features_x86.hpp +1121 -0
- package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpuid.hpp +144 -0
- package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_getauxval.hpp +159 -0
- package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_macros.hpp +78 -0
- package/third-party/common/phonemis/src/third-party/xsimd/math/xsimd_rem_pio2.hpp +707 -0
- package/third-party/common/phonemis/src/third-party/xsimd/memory/xsimd_aligned_allocator.hpp +349 -0
- package/third-party/common/phonemis/src/third-party/xsimd/memory/xsimd_alignment.hpp +102 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_all_registers.hpp +57 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_api.hpp +3106 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx2_register.hpp +57 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512bw_register.hpp +51 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512cd_register.hpp +51 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512dq_register.hpp +51 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512er_register.hpp +51 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512f_register.hpp +77 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512ifma_register.hpp +51 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512pf_register.hpp +51 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512vbmi2_register.hpp +51 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512vbmi_register.hpp +51 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512vnni_avx512bw_register.hpp +54 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512vnni_avx512vbmi2_register.hpp +53 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx_register.hpp +79 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avxvnni_register.hpp +44 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_batch.hpp +1666 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_batch_constant.hpp +572 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_batch_fwd.hpp +41 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_common_arch.hpp +49 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_emulated_register.hpp +80 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_fma3_avx2_register.hpp +50 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_fma3_avx_register.hpp +50 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_fma3_sse_register.hpp +50 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_fma4_register.hpp +50 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_i8mm_neon64_register.hpp +55 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_neon64_register.hpp +55 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_neon_register.hpp +130 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_register.hpp +96 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_rvv_register.hpp +491 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_sse2_register.hpp +59 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_sse3_register.hpp +49 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_sse4_1_register.hpp +48 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_sse4_2_register.hpp +48 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_ssse3_register.hpp +48 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_sve_register.hpp +216 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_traits.hpp +409 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_utils.hpp +462 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_vsx_register.hpp +84 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_vxe_register.hpp +86 -0
- package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_wasm_register.hpp +59 -0
- package/third-party/common/phonemis/src/third-party/xsimd/utils/bits.hpp +122 -0
- package/third-party/common/phonemis/src/third-party/xsimd/utils/xsimd_type_traits.hpp +145 -0
- package/third-party/common/phonemis/src/third-party/xsimd/xsimd.hpp +43 -0
- package/third-party/common/phonemis/src/phonemis/base/processor/constants.h +0 -11
- package/third-party/common/phonemis/src/phonemis/base/processor/num2word/config.h +0 -16
- package/third-party/common/phonemis/src/phonemis/base/processor/num2word/layer.cpp +0 -271
- package/third-party/common/phonemis/src/phonemis/base/processor/num2word/layer.h +0 -45
- package/third-party/common/phonemis/src/phonemis/base/processor/num2word/types.h +0 -29
- package/third-party/common/phonemis/src/phonemis/base/processor/sanitizer_layer.cpp +0 -22
- package/third-party/common/phonemis/src/phonemis/base/processor/trim_layer.cpp +0 -40
- package/third-party/common/phonemis/src/phonemis/base/processor/trim_layer.h +0 -22
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
# Phonemis: Code Style and Logic Modelling Notes
|
|
2
|
+
|
|
3
|
+
These notes describe how the author writes and structures code in `src/phonemis`. Use them to write
|
|
4
|
+
new code (a language, a component, a fix) that reads like the author wrote it.
|
|
5
|
+
|
|
6
|
+
## 0. Sources and weighting
|
|
7
|
+
|
|
8
|
+
- **Primary evidence** (hand-written by the author, confirmed with `git blame`):
|
|
9
|
+
- `base/`: pipeline, phonemizer core, lexicon and hybrid phonemizers, tokenizer, tagger
|
|
10
|
+
interface, processor and layers.
|
|
11
|
+
- `lang/en/*`, except `num2word.cpp`.
|
|
12
|
+
- Every `lang/*/pipeline.h`.
|
|
13
|
+
- `utils/*`.
|
|
14
|
+
- `protophone/{tensor,tensor_view,tensor_utils,types,meta,numeric}.h`.
|
|
15
|
+
- **Excluded or weighted lower:**
|
|
16
|
+
- `lang/*/num2word.cpp` and `protophone/protophone.cpp`, which are mostly AI-generated.
|
|
17
|
+
- The long linguistic commentary in the non-English `constants.h` files. It was written
|
|
18
|
+
together with the num2word code.
|
|
19
|
+
- Commit `c8d8744`, an external AI-assisted refactor. It rewrote `tagger/hmm_tagger.*` and
|
|
20
|
+
`nn/tokenizer.*` and split `num2word/layer.cpp` into helpers.
|
|
21
|
+
- Commit `36cef09`, which added `phonemize_words()` to `ipipeline.cpp`.
|
|
22
|
+
- The comment style of these commits is **not** the author's voice (see §9).
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 1. Modelling the logic (architecture)
|
|
27
|
+
|
|
28
|
+
### 1.1 Three-stage pipeline behind a facade
|
|
29
|
+
- Every language is an `IPipeline` with three overridable stages. The base `operator()` runs
|
|
30
|
+
them in order:
|
|
31
|
+
```cpp
|
|
32
|
+
class IPipeline {
|
|
33
|
+
public:
|
|
34
|
+
virtual ~IPipeline() = default;
|
|
35
|
+
virtual std::u32string operator()(std::u32string_view text, bool preprocess = true, bool postprocess = true);
|
|
36
|
+
// A processing parts to be implemented by derived classes.
|
|
37
|
+
virtual std::u32string preprocess(const std::u32string& input) = 0;
|
|
38
|
+
virtual std::u32string process(const std::u32string& input) = 0;
|
|
39
|
+
virtual std::u32string postprocess(const std::u32string& input) = 0;
|
|
40
|
+
};
|
|
41
|
+
```
|
|
42
|
+
- The public `Pipeline` class is a facade over `std::unique_ptr<IPipeline> impl_`:
|
|
43
|
+
- A private static factory, `create_pipeline(const Config&)`, chooses the implementation.
|
|
44
|
+
- The factory is a flat `if` chain on language codes. Each line ends with a comment naming the
|
|
45
|
+
language (`// German`).
|
|
46
|
+
- It throws `std::invalid_argument` for an empty or unsupported language.
|
|
47
|
+
- Every public method is a one-line delegation to `impl_`.
|
|
48
|
+
- Each stage is public on its own (`preprocess`/`process`/`postprocess`), as are the `preprocess`
|
|
49
|
+
and `postprocess` flags. This keeps stages testable and lets callers bypass them.
|
|
50
|
+
- There are UTF-8 (`std::string_view`) and UTF-32 (`std::u32string_view`) overloads of the entry
|
|
51
|
+
points. The UTF-8 overload converts the input and forwards to the UTF-32 one.
|
|
52
|
+
|
|
53
|
+
### 1.2 Template Method everywhere: the base owns the loop, the subclass handles one unit
|
|
54
|
+
| Base driver (implemented once) | Hooks for subclasses |
|
|
55
|
+
|---|---|
|
|
56
|
+
| `IPipeline::operator()` | `preprocess` / `process` / `postprocess` (`= 0`) |
|
|
57
|
+
| `Phonemizer::phonemize(std::span<const Token>)` | `phonemize(const Token&) const = 0`, `update_context(idx, tokens)` (no-op by default) |
|
|
58
|
+
| `Tagger::tag(std::span<Token>)` (splits the stream on EOS characters) | `tag_sentence(std::span<Token>) const = 0` |
|
|
59
|
+
| `Num2WordLayer<Reading>::apply()` (each pattern rewrites a draft: scan → `accept` → `resolve` → verbalize) | `resolve`, `read_number`, `read_code_digits` (`= 0`), `accept`, `read_date`, `read_money`, … (defaults) |
|
|
60
|
+
| `HMMTagger::tag_sentence()` | `should_lowercase(word)`: protected virtual, defaults to `false`, English overrides it |
|
|
61
|
+
|
|
62
|
+
- Per-unit hooks are `const` (pure lookups). All state changes go through one hook that is
|
|
63
|
+
explicitly non-const (`update_context`), and its `@details` says why:
|
|
64
|
+
> `@details Non-const as it manages stateful context updates across the sequence.`
|
|
65
|
+
> `@details Const as it is a pure lookup based on current internal state.`
|
|
66
|
+
|
|
67
|
+
### 1.3 A pipeline is a composition of submodules
|
|
68
|
+
```cpp
|
|
69
|
+
private:
|
|
70
|
+
// Required submodules
|
|
71
|
+
processor::Preprocessor preprocessor_;
|
|
72
|
+
tokenizer::Tokenizer tokenizer_;
|
|
73
|
+
phonemizer::HybridPhonemizer<en::LexiconPhonemizer, phonemizer::nn::NeuralPhonemizer> phonemizer_;
|
|
74
|
+
|
|
75
|
+
// Optional submodules
|
|
76
|
+
std::unique_ptr<tagger::HMMTagger> tagger_ = nullptr;
|
|
77
|
+
```
|
|
78
|
+
- Required parts are value members. Optional parts are a `unique_ptr`, created only when their
|
|
79
|
+
config is present and checked with `if (tagger_)`.
|
|
80
|
+
- `HybridPhonemizer` is a template constrained with C++20 concepts:
|
|
81
|
+
`template <std::derived_from<LexiconPhonemizer> LXP, std::derived_from<nn::NeuralPhonemizer> NNP>`.
|
|
82
|
+
- It owns both sub-phonemizers through `unique_ptr`.
|
|
83
|
+
- A sub-phonemizer exists only if its file path was given: "Not passing filepath … disables it".
|
|
84
|
+
- Lookup order is lexicon first, then the neural model as a fallback, written as ternaries on
|
|
85
|
+
the pointers.
|
|
86
|
+
|
|
87
|
+
### 1.4 Layered text processing (chain of transformations)
|
|
88
|
+
- The interface is `processor::Layer`, with
|
|
89
|
+
`virtual std::u32string transform(std::u32string_view input) const = 0;`.
|
|
90
|
+
- `Processor` holds a `std::vector<std::unique_ptr<Layer>>`, exposes `add_layer(...)`, and
|
|
91
|
+
`process()` applies the layers in order.
|
|
92
|
+
- Role names are aliases, not new types:
|
|
93
|
+
`using Preprocessor = Processor; using Postprocessor = Processor; // Naming abstraction`.
|
|
94
|
+
- Generic layers are configured with callables rather than subclasses. `SanitizerLayer(Filter, Mapper)`
|
|
95
|
+
takes `std::function` aliases whose defaults are identity lambdas. Language pipelines pass small
|
|
96
|
+
lambdas that look up a table from `constants.h`.
|
|
97
|
+
|
|
98
|
+
### 1.5 Language specialisation is data first, subclasses second
|
|
99
|
+
- Generic components receive language data as non-owning pointers to static tables:
|
|
100
|
+
`tokenizer_(&constants::tokenizer::kSpecialCharacters, &constants::tokenizer::kExceptions)`.
|
|
101
|
+
- A language is mostly tables in `lang/xx/constants.h`: split rules, exception words, number
|
|
102
|
+
words, currencies, months, and character replacements.
|
|
103
|
+
- Subclass only where tables are not enough (`en::LexiconPhonemizer`, `en::HMMTagger`,
|
|
104
|
+
`xx::Num2Word`).
|
|
105
|
+
- A subclass **reuses the base class name inside the language namespace**: `phonemis::en::Pipeline`,
|
|
106
|
+
`phonemis::en::LexiconPhonemizer`, `phonemis::en::HMMTagger`.
|
|
107
|
+
- Constructors are inherited: `using tagger::HMMTagger::HMMTagger;`, `using Num2WordLayer::Num2WordLayer;`.
|
|
108
|
+
- The base class alias pattern:
|
|
109
|
+
```cpp
|
|
110
|
+
// A shortcut for base class calls
|
|
111
|
+
using Base = phonemizer::LexiconPhonemizer;
|
|
112
|
+
using Base::phonemize; // un-hide the overloaded base method
|
|
113
|
+
...
|
|
114
|
+
auto result = Base::lookup(utf8_word, tag);
|
|
115
|
+
```
|
|
116
|
+
- When a language needs a modified config, a small private static helper copies it and patches
|
|
117
|
+
only the missing fields (`hi::Pipeline::make_phonemizer_config`).
|
|
118
|
+
|
|
119
|
+
### 1.6 Configuration modelling
|
|
120
|
+
- Each module has one `struct Config` in its own `config.h`, inside the module's namespace
|
|
121
|
+
(`phonemis::Config`, `phonemizer::Config`, `tagger::Config`, `num2word::Config`).
|
|
122
|
+
- The top-level `Config` aggregates sub-configs. A required sub-config is a plain member; an
|
|
123
|
+
optional one is wrapped: `std::optional<tagger::Config> tagger;`.
|
|
124
|
+
- Optional resources are `std::optional<std::string> xxx_filepath = std::nullopt;`. A missing
|
|
125
|
+
resource **disables the feature**, documented as `@details If not provided, … is disabled.`
|
|
126
|
+
- Overrides of default tables are raw pointers to externally owned data, defaulting to null:
|
|
127
|
+
`const std::unordered_map<char32_t, int64_t>* nn_grapheme_mapping = nullptr;`.
|
|
128
|
+
- Every field has its own `/** … */` block, with a blank line between fields.
|
|
129
|
+
|
|
130
|
+
### 1.7 Plain data structs carry state between stages
|
|
131
|
+
- `Token` holds the text, context flags (`first`, `whitespace`) and optional decorations
|
|
132
|
+
(`std::optional<tagger::Tag> tag`).
|
|
133
|
+
- Decorator modules such as the tagger annotate tokens **in place**, rather than producing
|
|
134
|
+
parallel arrays.
|
|
135
|
+
- Position is stored as flags, not offsets.
|
|
136
|
+
- Small named structs are used instead of tuples or out-parameters: `NumberMatch {len, mode}`,
|
|
137
|
+
`GlobalContext`, `Weights<T> {weight, bias}`, `Range<T> {begin, end}`. Every field gets a default
|
|
138
|
+
member initializer.
|
|
139
|
+
|
|
140
|
+
### 1.8 Enums encode rules, and their order means something
|
|
141
|
+
- `enum class` is used for rule sets and modes: `split::Rule`, `num2word::Mode`,
|
|
142
|
+
`stress::Level : int32_t`.
|
|
143
|
+
- Each enumerator gets a short doc comment, often with an example:
|
|
144
|
+
`// Separator attached to the start of the following word (e.g., "well", "-being")`.
|
|
145
|
+
- Values are chosen so that comparisons make sense: `UNSTRESSED = -3 … NORMAL = 0 … FORCED = 3`
|
|
146
|
+
allows `*stress >= stress::Level::NORMAL`. The first enumerator is explicitly `= 0`.
|
|
147
|
+
- Blank lines group related enumerators (`POTENTIALLY_ORDINAL, ORDINAL` / `FRACTION, CURRENCY`).
|
|
148
|
+
|
|
149
|
+
### 1.9 Stateless logic goes in free functions in a namespace
|
|
150
|
+
- Example: `en::stress::apply(std::u32string& phonemes, Level)` and `en::stress::restress(...)`.
|
|
151
|
+
They are in-place and have no class around them.
|
|
152
|
+
- All utilities are namespaces of free functions, often templates: `utils::strings`,
|
|
153
|
+
`utils::unicode`, `utils::conversions`, `utils::io`, `protophone::numeric`, `protophone::meta`,
|
|
154
|
+
`protophone::utils`.
|
|
155
|
+
|
|
156
|
+
### 1.10 Choosing the backend at build time
|
|
157
|
+
- `#ifdef ET_ON` switches the includes, the member type (`std::unique_ptr<executorch::…::Module>`
|
|
158
|
+
vs `std::unique_ptr<protophone::Protophone>`), and the inference code. Both branches sit side by
|
|
159
|
+
side inside the same function.
|
|
160
|
+
|
|
161
|
+
### 1.11 Protophone (the in-house tensor library)
|
|
162
|
+
- The vocabulary mirrors PyTorch:
|
|
163
|
+
- `Tensor<T>` owns its data (`std::vector<T, xsimd::default_allocator<T>>`).
|
|
164
|
+
- `TensorView<T>` is non-owning: pointer, shape, strides and offset.
|
|
165
|
+
- `reshape`, `slice(args...)` and `transpose(dim0 = 0, dim1 = 1)` all return new views by value.
|
|
166
|
+
- Variadic slicing arguments are normalised to `std::variant<size_t, Range<size_t>, All>` in
|
|
167
|
+
`meta::to_slice_variant` (with `if constexpr`).
|
|
168
|
+
- In-place operations are free functions declared as friends (`utils::repack`) rather than member
|
|
169
|
+
methods.
|
|
170
|
+
- Comment headings group members: `// Friends declarations`, `// Tensor data`,
|
|
171
|
+
`// Tensor metadata`, `// Simple accessors` (one-line inline accessors).
|
|
172
|
+
- Forward declarations are collected in a commented block to break header cycles.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## 2. Domain logic patterns (G2P)
|
|
177
|
+
|
|
178
|
+
### 2.1 Cascade from most specific to most generic, with early returns
|
|
179
|
+
The order in `en::LexiconPhonemizer::phonemize`:
|
|
180
|
+
1. `lookup_special`: hand-coded special words (a/an/the/to/in/am/used/supposed/…), with context.
|
|
181
|
+
2. Text in all capitals → `lookup_nnp` (acronyms, spelled out letter by letter).
|
|
182
|
+
3. Exact lexicon entry (`is_known`), then apostrophe variants (`s'` → `'s`, trailing `'`).
|
|
183
|
+
4. Morphological stems: `lookup_stem_s`, then `lookup_stem_ed`, then `lookup_stem_ing`.
|
|
184
|
+
5. `std::nullopt`. The caller (`HybridPhonemizer`) falls back to the neural model, and the base
|
|
185
|
+
`Phonemizer` keeps punctuation.
|
|
186
|
+
|
|
187
|
+
Each step follows the same shape:
|
|
188
|
+
```cpp
|
|
189
|
+
phonemes = lookup_stem_s(token.text, tag, stress);
|
|
190
|
+
if (!phonemes.empty()) {
|
|
191
|
+
return std::make_optional(phonemes);
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### 2.2 Two ways to say "not found", by layer
|
|
196
|
+
- At the interface boundary, `Phonemizer::phonemize(const Token&)` returns
|
|
197
|
+
`std::optional<std::u32string>`, and `std::nullopt` means "cannot phonemize".
|
|
198
|
+
- Internal helpers (`lookup*`) return `std::u32string`, where an empty `U""` means "not found".
|
|
199
|
+
This keeps chains cheap. Convert to an optional only at the boundary.
|
|
200
|
+
- `std::optional<stress::Level>` means "leave stress unchanged" when empty, and
|
|
201
|
+
`std::optional<bool>` is used deliberately as a three-way value (`vowel_next`: vowel,
|
|
202
|
+
consonant, or unknown).
|
|
203
|
+
|
|
204
|
+
### 2.3 Lookups relax the context step by step
|
|
205
|
+
- Lexicon keys are `"word"` or `"word|context"`, where the context is the part-of-speech tag.
|
|
206
|
+
- `lookup(word, tag)` tries the exact tag, then the parent tag (`get_parent_tag`: `VB*` → `VERB`,
|
|
207
|
+
`NN*` → `NOUN`, …), then no context.
|
|
208
|
+
- Case also relaxes: exact case first, then lowercase (`is_known(word, try_lowercase = true)`).
|
|
209
|
+
|
|
210
|
+
### 2.4 Morphology is a stem lookup plus a phonological suffix rule
|
|
211
|
+
- First guard on length and suffix. Then an `if / else if` ladder finds a known stem. Then the stem
|
|
212
|
+
is phonemized, and a suffix allophone is chosen from its **last phoneme**.
|
|
213
|
+
- The small phoneme sets used for that choice are `static const std::u32string` locals tested with
|
|
214
|
+
`.find(c) != npos`:
|
|
215
|
+
```cpp
|
|
216
|
+
// https://en.wiktionary.org/wiki/-s
|
|
217
|
+
static const std::u32string hard_s_suffixes = U"ptkfθ";
|
|
218
|
+
static const std::u32string soft_s_suffixes = U"szʃʒʧʤ";
|
|
219
|
+
if (hard_s_suffixes.find(phonemes.back()) != std::u32string::npos) {
|
|
220
|
+
return phonemes + U"s";
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
- Dialect differences are a member flag set in the constructor (`british_(config.lang == "en-gb")`)
|
|
224
|
+
and resolved inline: `british_ ? U"ɪ" : U"ᵻ"`.
|
|
225
|
+
- IPA literals used only once stay inline (`U"ɾᵻd"`, `U"ɐm"`). They move to `constants.h` only
|
|
226
|
+
when shared or language-configurable.
|
|
227
|
+
|
|
228
|
+
### 2.5 Lookahead context
|
|
229
|
+
- `update_context(curr_token_id, tokens)` fills a member `GlobalContext` before each token:
|
|
230
|
+
- `vowel_next`: whether the next word starts with a vowel.
|
|
231
|
+
- `future_to`: scans the next 2 tokens that are not punctuation for "to".
|
|
232
|
+
- `phonemize` stays `const` and only reads the context.
|
|
233
|
+
|
|
234
|
+
### 2.6 Tokenization driven by rule tables
|
|
235
|
+
- The input is split into phrases on whitespace.
|
|
236
|
+
- **Hard** separators (non-alphanumeric characters with no rule) always split and become their own
|
|
237
|
+
tokens.
|
|
238
|
+
- **Soft** separators (keys in `split::Rules`) are resolved by their `Rule` in the recursive
|
|
239
|
+
`process_chunk`.
|
|
240
|
+
- `split::Exceptions` (matched case-insensitively) stop any further splitting.
|
|
241
|
+
- Scanning avoids copies. The buffer is an `(offset, len)` pair into the input, handed on as
|
|
242
|
+
`std::u32string_view` sub-views. Only the final tokens are copied into `std::u32string`.
|
|
243
|
+
- Assumptions about earlier stages are stated rather than re-checked:
|
|
244
|
+
`// We assume the input got preprocessed in a correct way (i.e. with TrimLayer)`.
|
|
245
|
+
|
|
246
|
+
### 2.7 num2word: scan → classify → verbalize
|
|
247
|
+
- The scanner finds a number at a word boundary and classifies it as a `Mode`. It then builds
|
|
248
|
+
`StringifiedNumber{text, mode}` and calls `verbalize`, which parses the text and dispatches to
|
|
249
|
+
the virtual primitives.
|
|
250
|
+
- Each language's decimal separator is first normalised to `.`, so a single scanner works for every
|
|
251
|
+
language.
|
|
252
|
+
- Languages override the primitives. They override `verbalize` itself only for grammar quirks
|
|
253
|
+
(for example, Spanish "de" in dates).
|
|
254
|
+
|
|
255
|
+
### 2.8 Punctuation and whitespace are kept by structure
|
|
256
|
+
- The base `Phonemizer::phonemize` appends:
|
|
257
|
+
- the phonemes;
|
|
258
|
+
- the raw punctuation character when the token was not phonemized (except a soft `.` or `-` in
|
|
259
|
+
the middle of a word);
|
|
260
|
+
- `' '` when `token.whitespace` is set.
|
|
261
|
+
- Output spacing therefore matches token boundaries.
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## 3. Code organisation
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
base/<module>/
|
|
269
|
+
config.h // struct Config
|
|
270
|
+
types.h // enums, aliases, small structs
|
|
271
|
+
constants.h // inline tables in <module>::constants
|
|
272
|
+
<module>.h/.cpp // interface / abstract base with the driver
|
|
273
|
+
<impl>_<module>.h/.cpp // concrete: lexicon_phonemizer, hmm_tagger
|
|
274
|
+
base/processor/<layer>/
|
|
275
|
+
layer.h/.cpp // one directory per layer: trim, sanitizer, term2word, num2word, sym2word
|
|
276
|
+
lang/<code>/
|
|
277
|
+
constants.h pipeline.h (header-only) num2word.h/.cpp [lexicon_phonemizer|hmm_tagger|stress].h/.cpp
|
|
278
|
+
```
|
|
279
|
+
- One responsibility per file; a file is named after its main class or concept (snake_case).
|
|
280
|
+
- These are header-only:
|
|
281
|
+
- Language pipelines (an explicit decision: "Make lang-specific pipelines header only").
|
|
282
|
+
- Templates (`HybridPhonemizer`, `strings.h`, tensors).
|
|
283
|
+
- `constexpr` Unicode helpers.
|
|
284
|
+
|
|
285
|
+
Non-template logic of real size goes in a `.cpp`.
|
|
286
|
+
- Namespaces mirror directories, **but `base/` is not a namespace level**:
|
|
287
|
+
- `base/tokenizer` → `phonemis::tokenizer`
|
|
288
|
+
- `base/phonemizer/nn` → `phonemis::phonemizer::nn`
|
|
289
|
+
- `lang/en` → `phonemis::en`
|
|
290
|
+
- `utils/strings.h` → `phonemis::utils::strings`
|
|
291
|
+
- Use nested namespace syntax: `namespace phonemis::phonemizer::nn {`. Always close with the full
|
|
292
|
+
name: `} // namespace phonemis::phonemizer::nn`.
|
|
293
|
+
- Constants live in `<ns>::constants`, split by which code consumes them: `constants::num2word`,
|
|
294
|
+
`constants::tokenizer`, `constants::alphabet`, `constants::ipa`, `constants::puncts`,
|
|
295
|
+
`constants::sanitizer`, `constants::ipa::stress`.
|
|
296
|
+
- In language `constants.h` files, each block opens with a banner such as
|
|
297
|
+
`// --- Tokenization Rules ---`.
|
|
298
|
+
- The **contents of the nested namespace are indented by 2**, and the block closes with
|
|
299
|
+
`} // namespace tokenizer`.
|
|
300
|
+
- Base types are imported with a fully qualified, leading-`::` using-declaration:
|
|
301
|
+
`using ::phonemis::tokenizer::split::Rule;`.
|
|
302
|
+
- File-local helpers go in an anonymous namespace at the top of the `.cpp`
|
|
303
|
+
(`// Helper functions` / `} // namespace`).
|
|
304
|
+
- Use `#pragma once` (not include guards).
|
|
305
|
+
|
|
306
|
+
**Include order**
|
|
307
|
+
```cpp
|
|
308
|
+
#include "constants.h" // 1. local, quoted, alphabetical (own header not forced first)
|
|
309
|
+
#include "lexicon_phonemizer.h"
|
|
310
|
+
#include "stress.h"
|
|
311
|
+
#include <phonemis/base/phonemizer/constants.h> // 2. project headers, <phonemis/...> path from src/
|
|
312
|
+
#include <phonemis/utils/strings.h>
|
|
313
|
+
|
|
314
|
+
#include <algorithm> // 3. standard library, alphabetical
|
|
315
|
+
#include <string>
|
|
316
|
+
|
|
317
|
+
#ifdef ET_ON // 4. backend / conditional includes last
|
|
318
|
+
```
|
|
319
|
+
- Inside `base/`, sibling modules are included by relative path (`"../tokenizer/token.h"`,
|
|
320
|
+
`"../types.h"`).
|
|
321
|
+
- Crossing into `utils/`, `lang/` or `protophone/` uses `<phonemis/...>`.
|
|
322
|
+
- Third-party headers use `<third-party/json.hpp>`.
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## 4. Naming
|
|
327
|
+
|
|
328
|
+
| Entity | Convention | Examples |
|
|
329
|
+
|---|---|---|
|
|
330
|
+
| Classes / structs | PascalCase; acronyms stay upper case | `LexiconPhonemizer`, `TensorView`, `HMMTagger`, `GlobalContext` |
|
|
331
|
+
| Interfaces | `I` prefix only on the top-level pipeline interface | `IPipeline`; other abstract bases have no prefix (`Phonemizer`, `Tagger`, `Layer`) |
|
|
332
|
+
| Functions, methods, locals, params | snake_case | `update_context`, `lookup_stem_ing`, `curr_token_id`, `token_vec` |
|
|
333
|
+
| Non-public data members | snake_case + trailing `_` | `dict_`, `rules_`, `impl_`, `british_`, `global_context_` |
|
|
334
|
+
| Public struct fields | snake_case, no suffix | `text`, `whitespace`, `lexicon_filepath`, `phoneme_offset` |
|
|
335
|
+
| Constants | `k` + PascalCase, `inline const` / `inline constexpr` | `kPrimary`, `kEosCharacters`, `kSpecialCharacters`, `kCardinals` |
|
|
336
|
+
| Enumerators | SCREAMING_SNAKE | `KEEP_WITH_RIGHT`, `POTENTIALLY_ORDINAL`, `EMPHASIZED` |
|
|
337
|
+
| Type aliases | PascalCase domain words, even for trivial aliases | `using Lang = std::string;`, `using Tag = std::string;`, `Filter`, `Mapper`, `Rules`, `Exceptions`, `Buffer` |
|
|
338
|
+
| Template params | short | `T`, `U`, `StringT`, `CharT`, `Pred`, `LXP`, `NNP` |
|
|
339
|
+
| Macros | SCREAMING_SNAKE | `MAKE_NON_INPLACE`, `REGISTER_TEST`, `ET_ON` |
|
|
340
|
+
|
|
341
|
+
- **In-place (mutating) string operations end in `__`**: `to_lower__`, `remove__`, `replace__`,
|
|
342
|
+
`filter__`. The non-mutating version has the plain name and is generated by
|
|
343
|
+
`MAKE_NON_INPLACE(name)`.
|
|
344
|
+
- The string utilities borrow Python's vocabulary: `strip`, `split`, `capitalize`, `is_alpha`,
|
|
345
|
+
`starts_with`, `ends_with`.
|
|
346
|
+
- Predicates start with `is_`, `has_` or `should_` (`is_known`, `is_soft_separator`,
|
|
347
|
+
`has_primary`, `should_lowercase`). Boolean flags are adjectives or nouns: `british_`, `first`,
|
|
348
|
+
`whitespace`, `vowel_next`, `future_to`, `last_was_space`.
|
|
349
|
+
- Variants of one operation share a stem and add a suffix: `lookup`, `lookup_and_restress`,
|
|
350
|
+
`lookup_stem_s|_ed|_ing`, `lookup_nnp`, `lookup_special`.
|
|
351
|
+
- Short or abbreviated local names are normal:
|
|
352
|
+
- `currw_offset` / `currw_len` (current word)
|
|
353
|
+
- `fp` (file path)
|
|
354
|
+
- `bpoint`, `lbound` / `rbound`
|
|
355
|
+
- `no_steps` / `no_classes` ("number of")
|
|
356
|
+
- `p1` / `p2` / `p3`, `dec_sep`, `idx`, `it`
|
|
357
|
+
- Prefixes and suffixes carry meaning:
|
|
358
|
+
- `nn_` marks neural-network config fields (`nn_model_filepath`).
|
|
359
|
+
- `*_filepath` marks paths.
|
|
360
|
+
- Converted copies are named after what they are: `utf8_text`, `lower_word`, `word_stripped`,
|
|
361
|
+
`word_without_dots`.
|
|
362
|
+
- Legacy: `nn/constants.h` uses SCREAMING constants (`MAX_SEQ_LEN`, `PAD_TOKEN`,
|
|
363
|
+
`DEFAULT_CHAR_TO_TOKEN`). New constants should use `kPascalCase`.
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## 5. Formatting
|
|
368
|
+
|
|
369
|
+
- **Indentation: 2 spaces.** This is the dominant style; use it for new code. Some legacy files use
|
|
370
|
+
tabs (`tokenizer.cpp/.h`, base `lexicon_phonemizer.cpp`, `io.cpp`, `strings.h`) or 4 spaces
|
|
371
|
+
(`nn/tokenizer.h`, `num2word/layer.h`, `lang/*/num2word.h`, `main.cpp`, tests). When editing
|
|
372
|
+
those, match the surrounding lines.
|
|
373
|
+
- Braces are attached (K&R) for functions, classes and control flow. `public:` / `protected:` /
|
|
374
|
+
`private:` line up with `class`.
|
|
375
|
+
- Short guard clauses can be one line without braces: `if (chunk.empty()) return;`,
|
|
376
|
+
`if (t == 0) break;`, `if (!val) return U"";`. Everything else gets braces.
|
|
377
|
+
- When every branch of an `else if` ladder has its own comment, `}` goes on its own line and
|
|
378
|
+
`else if (...)` starts the next one (see `stress::apply`, `lookup_stem_ed`). Otherwise use
|
|
379
|
+
`} else if (...) {`.
|
|
380
|
+
- Long conditions break after `&&` or `||` and align with the opening parenthesis. Long parameter
|
|
381
|
+
lists are wrapped and aligned to the first parameter.
|
|
382
|
+
- `&` and `*` attach to the type: `const Config& config`, `T* data_`.
|
|
383
|
+
- Constructor initializer lists start on the next line with an indented `: `, one member per line
|
|
384
|
+
when they are long.
|
|
385
|
+
- Lookup tables are aligned in columns and pack several entries per line. Trailing comments in
|
|
386
|
+
structs are aligned too:
|
|
387
|
+
```cpp
|
|
388
|
+
bool first = false; // True if it's the first token in a sentence.
|
|
389
|
+
bool whitespace = false; // True if there is trailing whitespace implicitly included.
|
|
390
|
+
```
|
|
391
|
+
- Leave a blank line after the opening `namespace …{` line and before the closing one. Inside a
|
|
392
|
+
function, logical blocks are separated by a blank line plus a comment.
|
|
393
|
+
- There is no hard column limit; lines of about 120–130 characters are common.
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## 6. Comments and documentation
|
|
398
|
+
|
|
399
|
+
- **Public API in headers** uses Javadoc-style `/** … */` with `@param`, `@returns` / `@return`,
|
|
400
|
+
`@details`, `@throws`, `@tparam` and `@ref`.
|
|
401
|
+
- The first sentence is brief and the details follow.
|
|
402
|
+
- Parameter descriptions are short, often lowercase, often with an article:
|
|
403
|
+
`@param text an input text (utf-8) to be processed.`
|
|
404
|
+
- Simple members get a single `//` line, or nothing:
|
|
405
|
+
`// Performs a preprocessing stage of the pipeline.`. Groups of overrides sit under
|
|
406
|
+
`// Base class overrides`.
|
|
407
|
+
- **Voice:**
|
|
408
|
+
- Descriptions often start with an article: "A lexicon based phonemizer, which utilizes …",
|
|
409
|
+
"A shortcut for base class calls", "A (word | context) -> phonemes mapping.", "An interface
|
|
410
|
+
which allows to dynamically resolve pipelines …".
|
|
411
|
+
- Rationale and assumptions use "we": "We store both keys and values as utf-8 strings to save
|
|
412
|
+
memory comparing to u32string keys".
|
|
413
|
+
- Comments are short (1–3 lines), practical and candid: `// A hack to omit the leading
|
|
414
|
+
whitespaces`, `// Heuristic allocation`, `// WARNING: This file is called 'big szpont'`.
|
|
415
|
+
- `// NOTE:` marks behavioural caveats.
|
|
416
|
+
- **Numbered steps** in algorithms and setup code:
|
|
417
|
+
- `// 1. Context Update`, `// 2. Core Phonemization`, `// 3. Fallback/Punctuation Logic`,
|
|
418
|
+
`// 4. Whitespace handling`
|
|
419
|
+
- `// Step 0: Initialization`
|
|
420
|
+
- Pipelines: `// 1. Setup Preprocessing layers`, `// 2. Setup PoS tagger if specified`;
|
|
421
|
+
`// 1. Tokenize`, `// 2. Apply Part-of-Speech tags`,
|
|
422
|
+
`// 3. Generate phonemes (Lexicon with Neural fallback)`.
|
|
423
|
+
- **Examples inside comments:** `// xyz'abc -> xyz, 'abc (if xyz not empty)`,
|
|
424
|
+
`// '110xxxxx' pattern`, `(e.g., "it'", "s")`.
|
|
425
|
+
- External references are bare URLs: `// https://en.wiktionary.org/wiki/-ed`.
|
|
426
|
+
- Non-ASCII literals are annotated with a name and code point:
|
|
427
|
+
`U'।', // Hindi Purna Viram (U+0964)`.
|
|
428
|
+
- **Section banners:**
|
|
429
|
+
- utilities: `// ----------------------` / `// ----- Predicates -----` / `// ----------------------`
|
|
430
|
+
- language constants: `// --- Number to Word Normalization ---`
|
|
431
|
+
- long class bodies: `// ---- Weight loading ----…`
|
|
432
|
+
- Utility headers have a doc block above the namespace describing the whole module
|
|
433
|
+
(`/** Type conversions … */`,
|
|
434
|
+
`/** String utilities. … NOTE: Inline (mutable) versions … trailing __. */`).
|
|
435
|
+
- In dense declaration lists, end-of-line comments describe each item: the `lookup_*`
|
|
436
|
+
declarations in `en/lexicon_phonemizer.h`, and buffer shapes in protophone (`// S x L`).
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
## 7. C++ idioms and technical choices
|
|
441
|
+
|
|
442
|
+
- **C++20, used where it helps:**
|
|
443
|
+
- concepts (`std::derived_from`), `std::span`, `contains()`
|
|
444
|
+
- designated initializers (`{.data_filepath = argv[++i]}`)
|
|
445
|
+
- structured bindings (`for (const auto& [key, value] : json_obj.items())`)
|
|
446
|
+
- `if` with an initializer (`if (auto it = m.find(c); it != m.end())`)
|
|
447
|
+
- `if constexpr`, and CTAD (`std::basic_string_view suffix_view{suffix}`)
|
|
448
|
+
- Algorithms: the author's own code mostly uses classic iterator pairs (`std::find_if`,
|
|
449
|
+
`std::any_of(begin, end, …)`) plus some `std::ranges::*`. Both are fine.
|
|
450
|
+
- **Text model: UTF-32 inside, UTF-8 at the edges.**
|
|
451
|
+
- Internal text is `std::u32string`, `std::u32string_view`, `char32_t`, `U'x'` and `U"…"`.
|
|
452
|
+
- UTF-8 appears only in the public API overloads, JSON loading and lexicon storage.
|
|
453
|
+
Dictionaries store UTF-8 keys and values to save memory and convert on lookup.
|
|
454
|
+
- **No dependencies:**
|
|
455
|
+
- Unicode handling is in-house: a lookup table (`unicode_table.h`) plus `constexpr inline`
|
|
456
|
+
predicates (`unicode::isalpha`, `tolower`, …), not `<locale>`.
|
|
457
|
+
- Generic helpers use a paired `using` so the same code works for `char` and `char32_t`:
|
|
458
|
+
`using std::isalpha; using unicode::isalpha;`.
|
|
459
|
+
- **Parameter passing:**
|
|
460
|
+
- `std::u32string_view` for read-only text.
|
|
461
|
+
- `const std::u32string&` when the callee needs a real string (map keys, `substr` plus
|
|
462
|
+
concatenation).
|
|
463
|
+
- `std::span<const Token>` vs `std::span<Token>` to signal read-only vs in-place.
|
|
464
|
+
- Small types by value. Return values by value.
|
|
465
|
+
- **Ownership:**
|
|
466
|
+
- Value members for parts that are always present.
|
|
467
|
+
- `std::unique_ptr<T> x_ = nullptr` (created with `std::make_unique`) for optional or
|
|
468
|
+
polymorphic parts.
|
|
469
|
+
- Raw `const T*` for static tables owned elsewhere, always null-checked
|
|
470
|
+
(`rules_ && rules_->contains(c)`).
|
|
471
|
+
- **Class hygiene:** `explicit` on one-argument constructors, `virtual ~Base() = default;`,
|
|
472
|
+
`override` on every override, `= default` where possible, and `const` on every query method.
|
|
473
|
+
- **Constants:**
|
|
474
|
+
- Tables are `inline const` containers in headers: `std::unordered_map` / `unordered_set`;
|
|
475
|
+
`std::map` where order matters (number scales); `std::vector` for index-addressed lists
|
|
476
|
+
(months, with `U""` at index 0); `std::array` for fixed-size sets.
|
|
477
|
+
- Scalars use `inline constexpr`.
|
|
478
|
+
- A constant derived from another is built with an immediately invoked lambda:
|
|
479
|
+
```cpp
|
|
480
|
+
inline const std::unordered_set<char32_t> kPunctuations = [] {
|
|
481
|
+
auto s = kNonQuotePunctations;
|
|
482
|
+
s.insert(U'"');
|
|
483
|
+
return s;
|
|
484
|
+
}();
|
|
485
|
+
```
|
|
486
|
+
- A small lookup string used in one function is a function-local `static const`.
|
|
487
|
+
- **Optionals:** `has_value()` / `value()` and `*opt` are both used. `value_or("")` supplies
|
|
488
|
+
defaults, and returns write `std::make_optional(...)` explicitly.
|
|
489
|
+
- **Performance defaults for mobile/edge CPUs:**
|
|
490
|
+
- `reserve()` with a rough size before loops (`tokens.size() * 5 // Heuristic allocation`,
|
|
491
|
+
`input.size() / 5`).
|
|
492
|
+
- Offset-and-length views instead of substring copies.
|
|
493
|
+
- Flat vectors and dense ids on hot paths.
|
|
494
|
+
- SIMD-aligned allocators and scratch buffers that only grow, in the neural engine.
|
|
495
|
+
- **`using`:**
|
|
496
|
+
- A `.cpp` may start with `using namespace utils;` inside the project namespace, then call
|
|
497
|
+
`strings::…`, `conversions::…`, `unicode::…`.
|
|
498
|
+
- Functions that use many constants add a function-scope `using namespace …::constants;` or a
|
|
499
|
+
targeted `using …::kPrimary;`.
|
|
500
|
+
- Headers only have targeted declarations (`using tokenizer::Token;`, `using tagger::Tag;`).
|
|
501
|
+
- Small predicates are inline lambdas, often with an explicit `-> bool`.
|
|
502
|
+
- Casts are explicit `static_cast`, and indices are `size_t`.
|
|
503
|
+
- Unused parameters keep their names in comments: `size_t /*curr_token_id*/`.
|
|
504
|
+
- Macros are used only to generate code (non-inplace wrappers, test registration).
|
|
505
|
+
|
|
506
|
+
---
|
|
507
|
+
|
|
508
|
+
## 8. Error handling and robustness
|
|
509
|
+
|
|
510
|
+
- **Throw only during construction and resource loading:**
|
|
511
|
+
- A missing required config throws `std::runtime_error`, formatted as
|
|
512
|
+
`"<Class>: <field> must be provided in the configuration."`.
|
|
513
|
+
- Bad arguments or malformed data throw `std::invalid_argument` (unsupported language, invalid
|
|
514
|
+
JSON, wrong value type in the lexicon or tokenizer JSON).
|
|
515
|
+
- I/O failures throw `std::runtime_error`.
|
|
516
|
+
- Validate once at load time, then trust the data on the hot path. For example, the lexicon is
|
|
517
|
+
checked to be string → string in the constructor.
|
|
518
|
+
- **Phonemization never throws:**
|
|
519
|
+
- An unknown word gives `std::nullopt` or `U""`, and the next fallback takes over.
|
|
520
|
+
- Characters not in the tokenizer mapping are skipped silently, with a `// NOTE:` saying so.
|
|
521
|
+
- Words that are too long are split recursively.
|
|
522
|
+
- Invalid UTF-8 bytes become U+FFFD.
|
|
523
|
+
- Exceptions are caught only at the outermost layer (`main.cpp`), or around third-party parsing,
|
|
524
|
+
where they are rethrown as `std::invalid_argument` with context added.
|
|
525
|
+
|
|
526
|
+
---
|
|
527
|
+
|
|
528
|
+
## 9. Not the author's voice: do not imitate
|
|
529
|
+
|
|
530
|
+
These patterns are in the tree but come from the external refactor (`c8d8744`, `36cef09`) or from
|
|
531
|
+
the AI-generated num2word code:
|
|
532
|
+
- Paragraph-long rationale comments on members. Examples: `token_to_char_` in `nn/tokenizer.h`,
|
|
533
|
+
the probability tables in `tagger/hmm_tagger.h`, and `phonemize_words` in `ipipeline.cpp`.
|
|
534
|
+
- Phrases such as "matching the previous behaviour", "mirroring the original behavior", "hot path",
|
|
535
|
+
"pure scanning: it does not mutate any state".
|
|
536
|
+
- Em dashes (—) and textbook grammar explanations in comments (non-English `constants.h`).
|
|
537
|
+
- Helpers split out for a single condition (`at_word_end`, `is_trailing_currency`).
|
|
538
|
+
|
|
539
|
+
The author's own version of these is a 1–3 line comment saying what the code does (and briefly
|
|
540
|
+
why), numbered steps, and an inline example.
|
|
541
|
+
|
|
542
|
+
---
|
|
543
|
+
|
|
544
|
+
## 10. Known inconsistencies
|
|
545
|
+
|
|
546
|
+
Use the dominant form in new code. Do not "fix" these in unrelated changes.
|
|
547
|
+
- Indentation: 2 spaces dominant; tabs and 4 spaces in legacy files.
|
|
548
|
+
- Constant naming: `kName` dominant; SCREAMING in `nn/constants.h` and `LOG_EPSILON`.
|
|
549
|
+
- Some closing namespace comments are stale (`} // namespace phonemis::tagger::constants` in
|
|
550
|
+
`tokenizer/constants.h`, `} // namespace phonemis::utilities::conversions`, `} // phonemis::en`).
|
|
551
|
+
Always write the correct full name.
|
|
552
|
+
- `@return` and `@returns` are both used. Some `@param` names are stale (`tokenVec` vs `token_vec`).
|
|
553
|
+
- `i++` and `++i` are both used. Many files have no trailing newline at EOF.
|
|
554
|
+
|
|
555
|
+
---
|
|
556
|
+
|
|
557
|
+
## 11. Checklists
|
|
558
|
+
|
|
559
|
+
### Adding a language `xx`
|
|
560
|
+
1. Create `lang/xx/constants.h`, using `namespace phonemis::xx::constants`:
|
|
561
|
+
- `// --- Number to Word Normalization ---` → `namespace num2word { kCardinals, kLargeCardinals (std::map), kCurrencies, kMonths (index 0 = U""), kOrdinalSuffixes }`
|
|
562
|
+
- `// --- Tokenization Rules ---` → `namespace tokenizer { using ::phonemis::tokenizer::split::Rule; using …::Exceptions; kSpecialCharacters; kExceptions }`
|
|
563
|
+
2. Create `lang/xx/num2word.h/.cpp`:
|
|
564
|
+
- Declare `class Num2Word : public processor::num2word::Num2WordLayer` with
|
|
565
|
+
`using Num2WordLayer::Num2WordLayer;`.
|
|
566
|
+
- Define `decimal_separator()` inline in the header.
|
|
567
|
+
- Declare the remaining primitives with `override`.
|
|
568
|
+
3. Create a header-only `lang/xx/pipeline.h` following this skeleton:
|
|
569
|
+
```cpp
|
|
570
|
+
namespace phonemis::xx {
|
|
571
|
+
|
|
572
|
+
class Pipeline : public IPipeline {
|
|
573
|
+
public:
|
|
574
|
+
explicit Pipeline(const Config& config)
|
|
575
|
+
: tokenizer_(&constants::tokenizer::kSpecialCharacters,
|
|
576
|
+
&constants::tokenizer::kExceptions),
|
|
577
|
+
phonemizer_(config.phonemizer) {
|
|
578
|
+
|
|
579
|
+
// 1. Setup Preprocessing layers
|
|
580
|
+
preprocessor_.add_layer(std::make_unique<processor::trim::TrimLayer>());
|
|
581
|
+
preprocessor_.add_layer(std::make_unique<Num2Word>());
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Performs a preprocessing stage of the pipeline.
|
|
585
|
+
std::u32string preprocess(const std::u32string& input) override {
|
|
586
|
+
return preprocessor_.process(input);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Performs a middle stage of the pipeline - including phonemization.
|
|
590
|
+
std::u32string process(const std::u32string& input) override {
|
|
591
|
+
// 1. Tokenize
|
|
592
|
+
auto tokens = tokenizer_.tokenize(input);
|
|
593
|
+
|
|
594
|
+
// 2. Generate phonemes (Lexicon with Neural fallback)
|
|
595
|
+
return phonemizer_.phonemize(tokens);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// Performs a postprocessing stage of the pipeline.
|
|
599
|
+
std::u32string postprocess(const std::u32string& input) override {
|
|
600
|
+
// No postprocessing
|
|
601
|
+
return input;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
private:
|
|
605
|
+
// Required submodules
|
|
606
|
+
processor::Preprocessor preprocessor_;
|
|
607
|
+
tokenizer::Tokenizer tokenizer_;
|
|
608
|
+
phonemizer::HybridPhonemizer<phonemizer::LexiconPhonemizer, phonemizer::nn::NeuralPhonemizer> phonemizer_;
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
} // namespace phonemis::xx
|
|
612
|
+
```
|
|
613
|
+
4. Register the language in `base/pipeline.cpp`: add `#include <phonemis/lang/xx/pipeline.h>` and
|
|
614
|
+
`if (config.lang == "xx") { // Name` returning `std::make_unique<xx::Pipeline>(config)`.
|
|
615
|
+
5. Put data in `data/xx/`, and add tests (`test/num2word_xx_test.cpp`, …).
|
|
616
|
+
|
|
617
|
+
### Adding a component to `base/`
|
|
618
|
+
- Write an abstract base whose non-virtual driver loops over units and calls a pure virtual,
|
|
619
|
+
`const` per-unit hook. Add `virtual ~X() = default;`.
|
|
620
|
+
- Put `struct Config` in `config.h`. Document every field; optional resources are
|
|
621
|
+
`std::optional<std::string> …_filepath = std::nullopt`, and a missing one disables the feature.
|
|
622
|
+
- Write the concrete implementation as `<impl>_<component>.h/.cpp` with
|
|
623
|
+
`explicit X(const Config& config);`. The constructor throws if a required config value is
|
|
624
|
+
missing and validates loaded data once.
|
|
625
|
+
- Put tables in `constants.h` (`kName`, `inline const`), and enums and aliases in `types.h`.
|
|
626
|
+
- Plug the component into the language pipelines as a value member if required, or as a
|
|
627
|
+
`unique_ptr` (`= nullptr`) if optional.
|
|
628
|
+
|
|
629
|
+
---
|
|
630
|
+
|
|
631
|
+
## Appendix: tests (`test/`)
|
|
632
|
+
- The harness is in-house and has no dependencies (`test/test.h`).
|
|
633
|
+
- `REGISTER_TEST(name) { …; return true; }` defines a test.
|
|
634
|
+
- `ASSERT_EQUALS(expected, real)` prints a coloured message with file and line, then returns
|
|
635
|
+
`false`.
|
|
636
|
+
- A single runner lives in `test/test.cpp`.
|
|
637
|
+
- There is one file per component and variant, named `<component>_<base|lang>_test.cpp`
|
|
638
|
+
(`tokenizer_base_test.cpp`, `num2word_pl_test.cpp`).
|
|
639
|
+
- Test names follow `<component>_<variant>_<scenario>_test`
|
|
640
|
+
(`tokenizer_base_basic_tokenization_test`).
|
|
641
|
+
- Test code is older and formatted differently: 4-space indent, function braces on a new line, and
|
|
642
|
+
helper wrapper structs with `operator==` / `operator<<` for readable failures.
|