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.
Files changed (237) hide show
  1. package/lib/module/fetcher/telemetry.js +1 -1
  2. package/package.json +1 -1
  3. package/react-native-executorch.podspec +2 -0
  4. package/src/fetcher/telemetry.ts +1 -1
  5. package/third-party/common/phonemis/CMakeLists.txt +70 -14
  6. package/third-party/common/phonemis/README.md +183 -33
  7. package/third-party/common/phonemis/THIRD_PARTY_NOTICES.md +57 -0
  8. package/third-party/common/phonemis/knowledge/code_style.md +642 -0
  9. package/third-party/common/phonemis/licenses/Unicode-3.0.txt +39 -0
  10. package/third-party/common/phonemis/licenses/misaki-Apache-2.0.txt +201 -0
  11. package/third-party/common/phonemis/licenses/nlohmann-json-MIT.txt +21 -0
  12. package/third-party/common/phonemis/licenses/xsimd-BSD-3-Clause.txt +29 -0
  13. package/third-party/common/phonemis/src/phonemis/base/config.h +6 -0
  14. package/third-party/common/phonemis/src/phonemis/base/ipipeline.cpp +75 -0
  15. package/third-party/common/phonemis/src/phonemis/base/ipipeline.h +21 -0
  16. package/third-party/common/phonemis/src/phonemis/base/phonemizer/constants.h +2 -5
  17. package/third-party/common/phonemis/src/phonemis/base/phonemizer/lexicon_phonemizer.cpp +3 -4
  18. package/third-party/common/phonemis/src/phonemis/base/phonemizer/nn/neural_phonemizer.cpp +17 -8
  19. package/third-party/common/phonemis/src/phonemis/base/phonemizer/nn/neural_phonemizer.h +4 -0
  20. package/third-party/common/phonemis/src/phonemis/base/phonemizer/nn/tokenizer.cpp +21 -6
  21. package/third-party/common/phonemis/src/phonemis/base/phonemizer/nn/tokenizer.h +13 -4
  22. package/third-party/common/phonemis/src/phonemis/base/pipeline.cpp +8 -0
  23. package/third-party/common/phonemis/src/phonemis/base/pipeline.h +19 -0
  24. package/third-party/common/phonemis/src/phonemis/base/processor/common/conditions.cpp +37 -0
  25. package/third-party/common/phonemis/src/phonemis/base/processor/common/conditions.h +45 -0
  26. package/third-party/common/phonemis/src/phonemis/base/processor/common/constants.h +10 -0
  27. package/third-party/common/phonemis/src/phonemis/base/processor/common/context.cpp +105 -0
  28. package/third-party/common/phonemis/src/phonemis/base/processor/common/context.h +49 -0
  29. package/third-party/common/phonemis/src/phonemis/base/processor/common/document.cpp +173 -0
  30. package/third-party/common/phonemis/src/phonemis/base/processor/common/document.h +67 -0
  31. package/third-party/common/phonemis/src/phonemis/base/processor/common/text.cpp +75 -0
  32. package/third-party/common/phonemis/src/phonemis/base/processor/common/text.h +60 -0
  33. package/third-party/common/phonemis/src/phonemis/base/processor/common/types.h +18 -0
  34. package/third-party/common/phonemis/src/phonemis/base/processor/config.h +53 -0
  35. package/third-party/common/phonemis/src/phonemis/base/processor/layer.h +9 -7
  36. package/third-party/common/phonemis/src/phonemis/base/processor/layers/normalize/constants.h +20 -0
  37. package/third-party/common/phonemis/src/phonemis/base/processor/layers/normalize/layer.cpp +89 -0
  38. package/third-party/common/phonemis/src/phonemis/base/processor/layers/normalize/layer.h +35 -0
  39. package/third-party/common/phonemis/src/phonemis/base/processor/layers/normalize/types.h +16 -0
  40. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/kind.h +40 -0
  41. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/layer.h +382 -0
  42. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/notation.h +30 -0
  43. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/numerals.cpp +51 -0
  44. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/numerals.h +31 -0
  45. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/pattern.h +30 -0
  46. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/pattern_set.cpp +17 -0
  47. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/pattern_set.h +31 -0
  48. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/patterns.cpp +811 -0
  49. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/patterns.h +285 -0
  50. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/primitives.cpp +64 -0
  51. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/primitives.h +23 -0
  52. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/types.h +94 -0
  53. package/third-party/common/phonemis/src/phonemis/base/processor/layers/sanitizer/layer.cpp +19 -0
  54. package/third-party/common/phonemis/src/phonemis/base/processor/{sanitizer_layer.h → layers/sanitizer/layer.h} +8 -8
  55. package/third-party/common/phonemis/src/phonemis/base/processor/layers/sym2word/layer.cpp +65 -0
  56. package/third-party/common/phonemis/src/phonemis/base/processor/layers/sym2word/layer.h +29 -0
  57. package/third-party/common/phonemis/src/phonemis/base/processor/layers/term2word/layer.cpp +170 -0
  58. package/third-party/common/phonemis/src/phonemis/base/processor/layers/term2word/layer.h +40 -0
  59. package/third-party/common/phonemis/src/phonemis/base/processor/layers/term2word/types.h +25 -0
  60. package/third-party/common/phonemis/src/phonemis/base/processor/layers/trim/layer.cpp +47 -0
  61. package/third-party/common/phonemis/src/phonemis/base/processor/layers/trim/layer.h +19 -0
  62. package/third-party/common/phonemis/src/phonemis/base/processor/preprocessor.cpp +60 -0
  63. package/third-party/common/phonemis/src/phonemis/base/processor/preprocessor.h +45 -0
  64. package/third-party/common/phonemis/src/phonemis/base/processor/processor.cpp +6 -7
  65. package/third-party/common/phonemis/src/phonemis/base/processor/processor.h +14 -10
  66. package/third-party/common/phonemis/src/phonemis/base/tagger/hmm_tagger.cpp +89 -52
  67. package/third-party/common/phonemis/src/phonemis/base/tagger/hmm_tagger.h +16 -9
  68. package/third-party/common/phonemis/src/phonemis/base/tokenizer/tokenizer.cpp +13 -12
  69. package/third-party/common/phonemis/src/phonemis/lang/de/constants.h +183 -19
  70. package/third-party/common/phonemis/src/phonemis/lang/de/num2word.cpp +355 -309
  71. package/third-party/common/phonemis/src/phonemis/lang/de/num2word.h +42 -17
  72. package/third-party/common/phonemis/src/phonemis/lang/de/patterns.cpp +56 -0
  73. package/third-party/common/phonemis/src/phonemis/lang/de/patterns.h +15 -0
  74. package/third-party/common/phonemis/src/phonemis/lang/de/pipeline.h +3 -3
  75. package/third-party/common/phonemis/src/phonemis/lang/en/constants.h +181 -30
  76. package/third-party/common/phonemis/src/phonemis/lang/en/hmm_tagger.cpp +5 -7
  77. package/third-party/common/phonemis/src/phonemis/lang/en/num2word.cpp +232 -135
  78. package/third-party/common/phonemis/src/phonemis/lang/en/num2word.h +41 -16
  79. package/third-party/common/phonemis/src/phonemis/lang/en/pipeline.h +3 -10
  80. package/third-party/common/phonemis/src/phonemis/lang/es/constants.h +182 -60
  81. package/third-party/common/phonemis/src/phonemis/lang/es/num2word.cpp +247 -212
  82. package/third-party/common/phonemis/src/phonemis/lang/es/num2word.h +38 -18
  83. package/third-party/common/phonemis/src/phonemis/lang/es/patterns.cpp +50 -0
  84. package/third-party/common/phonemis/src/phonemis/lang/es/patterns.h +15 -0
  85. package/third-party/common/phonemis/src/phonemis/lang/es/pipeline.h +3 -3
  86. package/third-party/common/phonemis/src/phonemis/lang/fr/constants.h +152 -26
  87. package/third-party/common/phonemis/src/phonemis/lang/fr/num2word.cpp +252 -179
  88. package/third-party/common/phonemis/src/phonemis/lang/fr/num2word.h +38 -15
  89. package/third-party/common/phonemis/src/phonemis/lang/fr/patterns.cpp +83 -0
  90. package/third-party/common/phonemis/src/phonemis/lang/fr/patterns.h +15 -0
  91. package/third-party/common/phonemis/src/phonemis/lang/fr/pipeline.h +3 -3
  92. package/third-party/common/phonemis/src/phonemis/lang/hi/constants.h +62 -10
  93. package/third-party/common/phonemis/src/phonemis/lang/hi/num2word.cpp +74 -123
  94. package/third-party/common/phonemis/src/phonemis/lang/hi/num2word.h +27 -16
  95. package/third-party/common/phonemis/src/phonemis/lang/hi/pipeline.h +5 -3
  96. package/third-party/common/phonemis/src/phonemis/lang/it/constants.h +122 -42
  97. package/third-party/common/phonemis/src/phonemis/lang/it/num2word.cpp +200 -207
  98. package/third-party/common/phonemis/src/phonemis/lang/it/num2word.h +30 -18
  99. package/third-party/common/phonemis/src/phonemis/lang/it/patterns.cpp +25 -0
  100. package/third-party/common/phonemis/src/phonemis/lang/it/patterns.h +15 -0
  101. package/third-party/common/phonemis/src/phonemis/lang/it/pipeline.h +3 -3
  102. package/third-party/common/phonemis/src/phonemis/lang/pl/constants.h +346 -77
  103. package/third-party/common/phonemis/src/phonemis/lang/pl/num2word.cpp +631 -246
  104. package/third-party/common/phonemis/src/phonemis/lang/pl/num2word.h +47 -15
  105. package/third-party/common/phonemis/src/phonemis/lang/pl/pipeline.h +6 -6
  106. package/third-party/common/phonemis/src/phonemis/lang/pt/constants.h +201 -0
  107. package/third-party/common/phonemis/src/phonemis/lang/pt/num2word.cpp +365 -0
  108. package/third-party/common/phonemis/src/phonemis/lang/pt/num2word.h +53 -0
  109. package/third-party/common/phonemis/src/phonemis/lang/pt/pipeline.h +4 -2
  110. package/third-party/common/phonemis/src/phonemis/main.cpp +42 -11
  111. package/third-party/common/phonemis/src/phonemis/protophone/meta.h +24 -0
  112. package/third-party/common/phonemis/src/phonemis/protophone/numeric.h +26 -0
  113. package/third-party/common/phonemis/src/phonemis/protophone/protophone.cpp +525 -0
  114. package/third-party/common/phonemis/src/phonemis/protophone/protophone.h +80 -0
  115. package/third-party/common/phonemis/src/phonemis/protophone/tensor.h +76 -0
  116. package/third-party/common/phonemis/src/phonemis/protophone/tensor_utils.h +89 -0
  117. package/third-party/common/phonemis/src/phonemis/protophone/tensor_view.h +125 -0
  118. package/third-party/common/phonemis/src/phonemis/protophone/types.h +43 -0
  119. package/third-party/common/phonemis/src/phonemis/utils/strings.h +20 -14
  120. package/third-party/common/phonemis/src/phonemis/utils/unicode.h +15 -1
  121. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_arithmetic.hpp +267 -0
  122. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_bit.hpp +232 -0
  123. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_cast.hpp +43 -0
  124. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_complex.hpp +112 -0
  125. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_details.hpp +386 -0
  126. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_logical.hpp +240 -0
  127. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_math.hpp +2601 -0
  128. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_memory.hpp +1006 -0
  129. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_rounding.hpp +72 -0
  130. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_swizzle.hpp +260 -0
  131. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_trigo.hpp +996 -0
  132. package/third-party/common/phonemis/src/third-party/xsimd/arch/utils/shifts.hpp +82 -0
  133. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx.hpp +2111 -0
  134. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx2.hpp +1591 -0
  135. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx2_128.hpp +170 -0
  136. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512bw.hpp +724 -0
  137. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512cd.hpp +28 -0
  138. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512dq.hpp +270 -0
  139. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512er.hpp +17 -0
  140. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512f.hpp +2919 -0
  141. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512ifma.hpp +17 -0
  142. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512pf.hpp +17 -0
  143. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512vbmi.hpp +76 -0
  144. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512vbmi2.hpp +130 -0
  145. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512vnni_avx512bw.hpp +17 -0
  146. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512vnni_avx512vbmi2.hpp +17 -0
  147. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx_128.hpp +164 -0
  148. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avxvnni.hpp +17 -0
  149. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_common.hpp +26 -0
  150. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_common_fwd.hpp +127 -0
  151. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_constants.hpp +393 -0
  152. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_emulated.hpp +787 -0
  153. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_fma3_avx.hpp +93 -0
  154. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_fma3_avx2.hpp +46 -0
  155. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_fma3_sse.hpp +97 -0
  156. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_fma4.hpp +92 -0
  157. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_i8mm_neon64.hpp +17 -0
  158. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_isa.hpp +148 -0
  159. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_neon.hpp +3580 -0
  160. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_neon64.hpp +1614 -0
  161. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_rvv.hpp +1515 -0
  162. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_scalar.hpp +1210 -0
  163. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_sse2.hpp +2333 -0
  164. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_sse3.hpp +66 -0
  165. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_sse4_1.hpp +510 -0
  166. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_sse4_2.hpp +44 -0
  167. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_ssse3.hpp +186 -0
  168. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_sve.hpp +1186 -0
  169. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_vsx.hpp +947 -0
  170. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_vxe.hpp +787 -0
  171. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_wasm.hpp +1819 -0
  172. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_arch.hpp +242 -0
  173. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_config.hpp +633 -0
  174. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpu_features.hpp +50 -0
  175. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpu_features_arm.hpp +132 -0
  176. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpu_features_ppc.hpp +54 -0
  177. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpu_features_riscv.hpp +95 -0
  178. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpu_features_s390x.hpp +56 -0
  179. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpu_features_x86.hpp +1121 -0
  180. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpuid.hpp +144 -0
  181. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_getauxval.hpp +159 -0
  182. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_macros.hpp +78 -0
  183. package/third-party/common/phonemis/src/third-party/xsimd/math/xsimd_rem_pio2.hpp +707 -0
  184. package/third-party/common/phonemis/src/third-party/xsimd/memory/xsimd_aligned_allocator.hpp +349 -0
  185. package/third-party/common/phonemis/src/third-party/xsimd/memory/xsimd_alignment.hpp +102 -0
  186. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_all_registers.hpp +57 -0
  187. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_api.hpp +3106 -0
  188. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx2_register.hpp +57 -0
  189. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512bw_register.hpp +51 -0
  190. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512cd_register.hpp +51 -0
  191. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512dq_register.hpp +51 -0
  192. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512er_register.hpp +51 -0
  193. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512f_register.hpp +77 -0
  194. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512ifma_register.hpp +51 -0
  195. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512pf_register.hpp +51 -0
  196. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512vbmi2_register.hpp +51 -0
  197. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512vbmi_register.hpp +51 -0
  198. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512vnni_avx512bw_register.hpp +54 -0
  199. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512vnni_avx512vbmi2_register.hpp +53 -0
  200. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx_register.hpp +79 -0
  201. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avxvnni_register.hpp +44 -0
  202. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_batch.hpp +1666 -0
  203. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_batch_constant.hpp +572 -0
  204. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_batch_fwd.hpp +41 -0
  205. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_common_arch.hpp +49 -0
  206. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_emulated_register.hpp +80 -0
  207. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_fma3_avx2_register.hpp +50 -0
  208. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_fma3_avx_register.hpp +50 -0
  209. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_fma3_sse_register.hpp +50 -0
  210. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_fma4_register.hpp +50 -0
  211. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_i8mm_neon64_register.hpp +55 -0
  212. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_neon64_register.hpp +55 -0
  213. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_neon_register.hpp +130 -0
  214. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_register.hpp +96 -0
  215. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_rvv_register.hpp +491 -0
  216. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_sse2_register.hpp +59 -0
  217. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_sse3_register.hpp +49 -0
  218. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_sse4_1_register.hpp +48 -0
  219. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_sse4_2_register.hpp +48 -0
  220. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_ssse3_register.hpp +48 -0
  221. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_sve_register.hpp +216 -0
  222. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_traits.hpp +409 -0
  223. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_utils.hpp +462 -0
  224. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_vsx_register.hpp +84 -0
  225. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_vxe_register.hpp +86 -0
  226. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_wasm_register.hpp +59 -0
  227. package/third-party/common/phonemis/src/third-party/xsimd/utils/bits.hpp +122 -0
  228. package/third-party/common/phonemis/src/third-party/xsimd/utils/xsimd_type_traits.hpp +145 -0
  229. package/third-party/common/phonemis/src/third-party/xsimd/xsimd.hpp +43 -0
  230. package/third-party/common/phonemis/src/phonemis/base/processor/constants.h +0 -11
  231. package/third-party/common/phonemis/src/phonemis/base/processor/num2word/config.h +0 -16
  232. package/third-party/common/phonemis/src/phonemis/base/processor/num2word/layer.cpp +0 -271
  233. package/third-party/common/phonemis/src/phonemis/base/processor/num2word/layer.h +0 -45
  234. package/third-party/common/phonemis/src/phonemis/base/processor/num2word/types.h +0 -29
  235. package/third-party/common/phonemis/src/phonemis/base/processor/sanitizer_layer.cpp +0 -22
  236. package/third-party/common/phonemis/src/phonemis/base/processor/trim_layer.cpp +0 -40
  237. package/third-party/common/phonemis/src/phonemis/base/processor/trim_layer.h +0 -22
@@ -0,0 +1,811 @@
1
+ #include "patterns.h"
2
+ #include "primitives.h"
3
+ #include <phonemis/utils/strings.h>
4
+ #include <phonemis/utils/unicode.h>
5
+
6
+ #include <algorithm>
7
+ #include <iterator>
8
+
9
+ namespace phonemis::processor::num2word {
10
+
11
+ using namespace utils;
12
+
13
+ namespace {
14
+ // What may not stand right before a number, besides letters and digits.
15
+ constexpr std::u32string_view kNumberBoundaries = U".,";
16
+
17
+ // What may not stand right before an amount: also a hyphen, as in i9-14900K.
18
+ constexpr std::u32string_view kAmountBoundaries = U".,-";
19
+
20
+ // Value of a short run of digits (a day, a month or an hour).
21
+ int small_value(std::u32string_view digits) {
22
+ int value = 0;
23
+ for (char32_t c : digits) {
24
+ value = value * 10 + static_cast<int>(c - U'0');
25
+ }
26
+ return value;
27
+ }
28
+
29
+ // The numbers of an amount at a position - a range (10-15) or a single number - in the order a regex tries them.
30
+ std::vector<std::vector<Number>> amounts_at(std::u32string_view text, size_t position, const Notation& notation) {
31
+ std::vector<std::vector<Number>> amounts;
32
+ if (!is_clear_before(text, position, kAmountBoundaries)) {
33
+ return amounts;
34
+ }
35
+
36
+ auto firsts = numbers_at(text, position, notation.group_separators, notation.decimal_separators);
37
+ for (const Number& first : firsts) {
38
+ size_t hyphen = first.span.end;
39
+ if (hyphen < text.size() && text[hyphen] == U'-') {
40
+ for (Number last : numbers_at(text, hyphen + 1, notation.group_separators, notation.decimal_separators)) {
41
+ amounts.push_back({first, std::move(last)});
42
+ }
43
+ }
44
+ }
45
+ for (const Number& number : firsts) {
46
+ amounts.push_back({number});
47
+ }
48
+ return amounts;
49
+ }
50
+
51
+ // Keys of a single capital letter (W, V), or all the others.
52
+ Keys single_letters(const Keys& keys, bool single) {
53
+ Keys selected;
54
+ std::ranges::copy_if(keys, std::back_inserter(selected), [single](const auto& key) {
55
+ return (key.size() == 1 && key[0] >= U'A' && key[0] <= U'Z') == single;
56
+ });
57
+ return selected;
58
+ }
59
+ } // namespace
60
+
61
+ // ----------------------------
62
+ // ----- Generic patterns -----
63
+ // ----------------------------
64
+
65
+ std::vector<std::unique_ptr<Pattern>> generic_patterns(const Notation& notation) {
66
+ std::vector<std::unique_ptr<Pattern>> patterns;
67
+ patterns.push_back(std::make_unique<IsoDatePattern>());
68
+ patterns.push_back(std::make_unique<InternationalPhonePattern>());
69
+ patterns.push_back(std::make_unique<VersionPattern>(notation));
70
+ patterns.push_back(std::make_unique<DifferencePattern>());
71
+ patterns.push_back(std::make_unique<SumPattern>());
72
+ patterns.push_back(std::make_unique<RangePattern>());
73
+ patterns.push_back(std::make_unique<DimensionPattern>(notation));
74
+ if (notation.fractions) {
75
+ patterns.push_back(std::make_unique<FractionPattern>());
76
+ }
77
+ patterns.push_back(std::make_unique<SignPattern>());
78
+ patterns.push_back(std::make_unique<TimePattern>(notation));
79
+ if (!notation.time_suffixes.empty()) {
80
+ patterns.push_back(std::make_unique<HourPattern>(notation));
81
+ }
82
+ patterns.push_back(std::make_unique<ScorePattern>());
83
+ patterns.push_back(std::make_unique<PrefixedMoneyPattern>(notation));
84
+ patterns.push_back(std::make_unique<MoneyPattern>(notation));
85
+ patterns.push_back(std::make_unique<ScalePattern>(notation));
86
+ patterns.push_back(std::make_unique<PercentPattern>(notation));
87
+ patterns.push_back(std::make_unique<UnitPattern>(notation));
88
+ patterns.push_back(std::make_unique<CompoundPattern>(notation));
89
+ patterns.push_back(std::make_unique<CodePattern>());
90
+ patterns.push_back(std::make_unique<NumberPattern>(notation));
91
+ return patterns;
92
+ }
93
+
94
+ std::optional<Match> IsoDatePattern::match(std::u32string_view text, size_t position) const {
95
+ if (!is_clear_before(text, position, U".,/-") || digits_end(text, position) != position + 4) {
96
+ return std::nullopt;
97
+ }
98
+
99
+ // YYYY-MM-DD, with months and days of one or two digits
100
+ std::vector<Number> parts = {digits_at(text, position)};
101
+ for (size_t p = position + 4; parts.size() < 3;) {
102
+ size_t end = digits_end(text, p + 1);
103
+ if (p >= text.size() || text[p] != U'-' || end == p + 1 || end > p + 3) {
104
+ return std::nullopt;
105
+ }
106
+ parts.push_back(digits_at(text, p + 1));
107
+ p = end;
108
+ }
109
+
110
+ size_t end = parts[2].span.end;
111
+ int month = small_value(parts[1].integer), day = small_value(parts[2].integer);
112
+ if (!is_clear_after(text, end) || is_digit_after(text, end, U".,/-") || month < 1 || month > 12 || day < 1 ||
113
+ day > 31) {
114
+ return std::nullopt;
115
+ }
116
+ return Match{
117
+ .kind = Kind::DATE, .span = {position, end}, .numbers = {parts[2], parts[1], parts[0]}, .separator = U'-'};
118
+ }
119
+
120
+ VersionPattern::VersionPattern(const Notation& notation) : Pattern(Kind::VERSION), group_(notation.group_separators) {}
121
+
122
+ std::optional<Match> VersionPattern::match(std::u32string_view text, size_t position) const {
123
+ if (!is_clear_before(text, position, kNumberBoundaries) || !unicode::isdigit(text[position])) {
124
+ return std::nullopt;
125
+ }
126
+
127
+ std::vector<Number> parts = {digits_at(text, position)};
128
+ while (parts.back().span.end + 1 < text.size() && text[parts.back().span.end] == U'.' &&
129
+ unicode::isdigit(text[parts.back().span.end + 1])) {
130
+ parts.push_back(digits_at(text, parts.back().span.end + 1));
131
+ }
132
+
133
+ size_t end = parts.back().span.end;
134
+ if (parts.size() < 3 || !is_clear_after(text, end)) {
135
+ return std::nullopt;
136
+ }
137
+
138
+ // 1.000.000 is a number
139
+ bool thousands =
140
+ strings::contains(group_, U'.') && parts[0].integer.size() <= 3 &&
141
+ std::all_of(parts.begin() + 1, parts.end(), [](const Number& part) { return part.integer.size() == 3; });
142
+ if (thousands) {
143
+ return std::nullopt;
144
+ }
145
+ return Match{.kind = Kind::VERSION, .span = {position, end}, .numbers = parts, .separator = U'.'};
146
+ }
147
+
148
+ std::optional<Match> DifferencePattern::match(std::u32string_view text, size_t position) const {
149
+ if (position == 0 || position + 3 >= text.size() || text.substr(position, 3) != U" - " ||
150
+ !unicode::isdigit(text[position - 1]) || !unicode::isdigit(text[position + 3])) {
151
+ return std::nullopt;
152
+ }
153
+ return Match{.kind = Kind::DIFFERENCE, .span = {position, position + 3}, .separator = U'-'};
154
+ }
155
+
156
+ std::optional<Match> SumPattern::match(std::u32string_view text, size_t position) const {
157
+ if (position == 0 || !unicode::isdigit(text[position - 1])) {
158
+ return std::nullopt;
159
+ }
160
+ size_t plus = position;
161
+ while (plus < text.size() && unicode::isspace(text[plus])) {
162
+ plus++;
163
+ }
164
+ size_t end = plus + 1;
165
+ while (end < text.size() && unicode::isspace(text[end])) {
166
+ end++;
167
+ }
168
+ if (plus >= text.size() || text[plus] != U'+' || end >= text.size() || !unicode::isdigit(text[end])) {
169
+ return std::nullopt;
170
+ }
171
+ return Match{.kind = Kind::SUM, .span = {position, end}, .separator = U'+'};
172
+ }
173
+
174
+ std::optional<Match> RangePattern::match(std::u32string_view text, size_t position) const {
175
+ if (position == 0 || !unicode::isdigit(text[position - 1])) {
176
+ return std::nullopt;
177
+ }
178
+
179
+ // A hyphen (normalization writes dashes between numbers as one: 2020 – 2022)
180
+ size_t end = position + 1;
181
+ if (text[position] != U'-' || !is_digit_after(text, end)) {
182
+ return std::nullopt;
183
+ }
184
+
185
+ // The digits on both sides
186
+ size_t start = position;
187
+ while (start > 0 && unicode::isdigit(text[start - 1])) {
188
+ start--;
189
+ }
190
+ return Match{.kind = Kind::RANGE,
191
+ .span = {position, end},
192
+ .numbers = {digits_at(text, start), digits_at(text, end)},
193
+ .separator = U'-'};
194
+ }
195
+
196
+ DimensionPattern::DimensionPattern(const Notation& notation)
197
+ : Pattern(Kind::DIMENSION), units_(single_letters(notation.units, false)) {}
198
+
199
+ std::optional<Match> DimensionPattern::match(std::u32string_view text, size_t position) const {
200
+ // A number on the left, possibly followed by a unit: 3x4, 3 m x 4
201
+ size_t number_end = 0;
202
+ if (position > 0 && unicode::isdigit(text[position - 1])) {
203
+ number_end = position;
204
+ } else {
205
+ for (const auto& unit : units_) {
206
+ if (position < unit.size() || text.substr(position - unit.size(), unit.size()) != unit) {
207
+ continue;
208
+ }
209
+ size_t start = position - unit.size();
210
+ start = start > 0 && unicode::isspace(text[start - 1]) ? start - 1 : start;
211
+ if (start > 0 && unicode::isdigit(text[start - 1])) {
212
+ number_end = start;
213
+ break;
214
+ }
215
+ }
216
+ }
217
+
218
+ // Not a hexadecimal number: 0x1F
219
+ if (number_end == 0 || (text[number_end - 1] == U'0' && is_clear_before(text, number_end - 1))) {
220
+ return std::nullopt;
221
+ }
222
+
223
+ size_t x = skip_space(text, position);
224
+ if (x >= text.size() || (text[x] != U'x' && text[x] != U'×')) {
225
+ return std::nullopt;
226
+ }
227
+ size_t end = skip_space(text, x + 1);
228
+ if (!is_digit_after(text, end)) {
229
+ return std::nullopt;
230
+ }
231
+ return Match{.kind = Kind::DIMENSION, .span = {position, end}, .separator = U'x'};
232
+ }
233
+
234
+ std::optional<Match> FractionPattern::match(std::u32string_view text, size_t position) const {
235
+ size_t slash = digits_end(text, position);
236
+ if (!is_clear_before(text, position, U"./") || slash == position || slash >= text.size() || text[slash] != U'/') {
237
+ return std::nullopt;
238
+ }
239
+
240
+ size_t end = digits_end(text, slash + 1);
241
+ if (end == slash + 1 || !is_clear_after(text, end, U"/") || is_digit_after(text, end, U".,")) {
242
+ return std::nullopt;
243
+ }
244
+ return Match{.kind = Kind::FRACTION,
245
+ .span = {position, end},
246
+ .numbers = {digits_at(text, position), digits_at(text, slash + 1)},
247
+ .separator = U'/'};
248
+ }
249
+
250
+ std::optional<Match> SignPattern::match(std::u32string_view text, size_t position) const {
251
+ if ((text[position] != U'-' && text[position] != U'−') || !is_clear_before(text, position, kNumberBoundaries) ||
252
+ !is_digit_after(text, position + 1)) {
253
+ return std::nullopt;
254
+ }
255
+ return Match{.kind = Kind::SIGN, .span = {position, position + 1}};
256
+ }
257
+
258
+ TimePattern::TimePattern(const Notation& notation)
259
+ : Pattern(Kind::TIME),
260
+ separators_(notation.time_separators),
261
+ suffixes_(notation.time_suffixes),
262
+ measures_(single_letters(notation.units, false)) {
263
+ measures_.push_back(U"min");
264
+ }
265
+
266
+ std::optional<Match> TimePattern::match(std::u32string_view text, size_t position) const {
267
+ size_t hours_end = digits_end(text, position);
268
+ if (!is_clear_before(text, position, kNumberBoundaries) || hours_end == position || hours_end > position + 2 ||
269
+ hours_end >= text.size()) {
270
+ return std::nullopt;
271
+ }
272
+
273
+ // A separator (18:30, 18.30), or an "h" with optional spaces around it (18h30, 18 h 30)
274
+ char32_t separator = 0;
275
+ size_t minutes = hours_end + 1;
276
+ if (text[hours_end] != U'h' && strings::contains(separators_, text[hours_end])) {
277
+ separator = text[hours_end];
278
+ } else if (size_t h = skip_space(text, hours_end);
279
+ strings::contains(separators_, U'h') && h < text.size() && text[h] == U'h') {
280
+ separator = U'h';
281
+ minutes = skip_space(text, h + 1);
282
+ }
283
+ if (separator == 0 || digits_end(text, minutes) < minutes + 2) {
284
+ return std::nullopt;
285
+ }
286
+
287
+ Match time{.kind = Kind::TIME,
288
+ .numbers = {digits_at(text, position),
289
+ {.span = {minutes, minutes + 2}, .integer = std::u32string(text.substr(minutes, 2))}},
290
+ .separator = separator};
291
+
292
+ // With a suffix first (10:30 pm), then without it
293
+ size_t suffix = skip_space(text, minutes + 2);
294
+ for (auto key : keys_at(text, suffix, suffixes_)) {
295
+ if (ends_at(text, suffix + key.size())) {
296
+ time.span = {position, suffix + key.size()};
297
+ time.suffix = key;
298
+ return time;
299
+ }
300
+ }
301
+ if (ends_at(text, minutes + 2)) {
302
+ time.span = {position, minutes + 2};
303
+ return time;
304
+ }
305
+ return std::nullopt;
306
+ }
307
+
308
+ bool TimePattern::ends_at(std::u32string_view text, size_t position) const {
309
+ if (!is_clear_after(text, position) || is_digit_after(text, position, U".,:")) {
310
+ return false;
311
+ }
312
+ for (size_t start : {position, skip_space(text, position)}) {
313
+ for (auto key : keys_at(text, start, measures_)) {
314
+ if (is_clear_after(text, start + key.size())) {
315
+ return false;
316
+ }
317
+ }
318
+ }
319
+ return true;
320
+ }
321
+
322
+ HourPattern::HourPattern(const Notation& notation) : Pattern(Kind::TIME), suffixes_(notation.time_suffixes) {}
323
+
324
+ std::optional<Match> HourPattern::match(std::u32string_view text, size_t position) const {
325
+ size_t hours_end = digits_end(text, position);
326
+ if (!is_clear_before(text, position, kNumberBoundaries) || hours_end == position || hours_end > position + 2) {
327
+ return std::nullopt;
328
+ }
329
+
330
+ size_t suffix = skip_space(text, hours_end);
331
+ for (auto key : keys_at(text, suffix, suffixes_)) {
332
+ if (is_clear_after(text, suffix + key.size())) {
333
+ return Match{.kind = Kind::TIME,
334
+ .span = {position, suffix + key.size()},
335
+ .numbers = {digits_at(text, position)},
336
+ .suffix = key};
337
+ }
338
+ }
339
+ return std::nullopt;
340
+ }
341
+
342
+ std::optional<Match> ScorePattern::match(std::u32string_view text, size_t position) const {
343
+ if (position == 0 || text[position] != U':' || !unicode::isdigit(text[position - 1]) ||
344
+ !is_digit_after(text, position + 1)) {
345
+ return std::nullopt;
346
+ }
347
+ return Match{.kind = Kind::SCORE, .span = {position, position + 1}, .separator = U':'};
348
+ }
349
+
350
+ PrefixedMoneyPattern::PrefixedMoneyPattern(const Notation& notation) : Pattern(Kind::MONEY), notation_(notation) {
351
+ std::ranges::copy_if(notation.currencies, std::back_inserter(currencies_), [](const auto& currency) {
352
+ return !std::ranges::all_of(currency, [](char32_t c) { return utils::unicode::isalpha(c); });
353
+ });
354
+ }
355
+
356
+ std::optional<Match> PrefixedMoneyPattern::match(std::u32string_view text, size_t position) const {
357
+ if (!is_clear_before(text, position)) {
358
+ return std::nullopt;
359
+ }
360
+
361
+ for (auto currency : keys_at(text, position, currencies_)) {
362
+ size_t start = skip_space(text, position + currency.size());
363
+ auto numbers = numbers_at(text, start, notation_.group_separators, notation_.decimal_separators);
364
+ if (numbers.empty()) {
365
+ continue;
366
+ }
367
+
368
+ // An optional scale after the amount: $1.5 million
369
+ Match money{
370
+ .kind = Kind::MONEY, .span = {position, numbers[0].span.end}, .numbers = {numbers[0]}, .unit = currency};
371
+ size_t scale = skip_space(text, money.span.end);
372
+ for (auto key : keys_at(text, scale, notation_.scales)) {
373
+ if (is_clear_after(text, scale + key.size())) {
374
+ money.scale = key;
375
+ money.span.end = scale + key.size();
376
+ break;
377
+ }
378
+ }
379
+ return money;
380
+ }
381
+ return std::nullopt;
382
+ }
383
+
384
+ MoneyPattern::MoneyPattern(const Notation& notation) : Pattern(Kind::MONEY), notation_(notation) {}
385
+
386
+ std::optional<Match> MoneyPattern::match(std::u32string_view text, size_t position) const {
387
+ for (auto& numbers : amounts_at(text, position, notation_)) {
388
+ size_t end = numbers.back().span.end;
389
+
390
+ // The currency, after an optional scale: 3 mln zł, 50$
391
+ auto currency_at = [&](size_t start, std::u32string_view scale) -> std::optional<Match> {
392
+ for (auto currency : keys_at(text, start, notation_.currencies)) {
393
+ if (is_clear_after(text, start + currency.size())) {
394
+ return Match{.kind = Kind::MONEY,
395
+ .span = {position, start + currency.size()},
396
+ .numbers = numbers,
397
+ .unit = currency,
398
+ .scale = scale};
399
+ }
400
+ }
401
+ return std::nullopt;
402
+ };
403
+
404
+ size_t scale = skip_space(text, end);
405
+ for (auto key : keys_at(text, scale, notation_.scales)) {
406
+ if (auto money = currency_at(skip_space(text, scale + key.size()), key)) {
407
+ return money;
408
+ }
409
+ }
410
+ if (auto money = currency_at(skip_space(text, end), U"")) {
411
+ return money;
412
+ }
413
+ }
414
+ return std::nullopt;
415
+ }
416
+
417
+ ScalePattern::ScalePattern(const Notation& notation) : Pattern(Kind::SCALE), notation_(notation) {}
418
+
419
+ std::optional<Match> ScalePattern::match(std::u32string_view text, size_t position) const {
420
+ for (auto& numbers : amounts_at(text, position, notation_)) {
421
+ size_t scale = skip_space(text, numbers.back().span.end);
422
+ for (auto key : keys_at(text, scale, notation_.scales)) {
423
+ if (is_clear_after(text, scale + key.size())) {
424
+ return Match{.kind = Kind::SCALE, .span = {position, scale + key.size()}, .numbers = numbers, .scale = key};
425
+ }
426
+ }
427
+ }
428
+ return std::nullopt;
429
+ }
430
+
431
+ PercentPattern::PercentPattern(const Notation& notation) : Pattern(Kind::PERCENT), notation_(notation) {}
432
+
433
+ std::optional<Match> PercentPattern::match(std::u32string_view text, size_t position) const {
434
+ for (auto& numbers : amounts_at(text, position, notation_)) {
435
+ size_t percent = skip_space(text, numbers.back().span.end);
436
+ if (percent >= text.size() || text[percent] != U'%') {
437
+ continue;
438
+ }
439
+
440
+ // Letters right after it belong to it: 8%ige
441
+ size_t end = percent + 1;
442
+ while (end < text.size() && unicode::isword(text[end])) {
443
+ end++;
444
+ }
445
+ return Match{.kind = Kind::PERCENT,
446
+ .span = {position, end},
447
+ .numbers = numbers,
448
+ .suffix = text.substr(percent + 1, end - percent - 1)};
449
+ }
450
+ return std::nullopt;
451
+ }
452
+
453
+ UnitPattern::UnitPattern(const Notation& notation)
454
+ : Pattern(Kind::UNIT), notation_(notation), letters_(single_letters(notation.units, true)) {
455
+ notation_.units = single_letters(notation.units, false);
456
+ }
457
+
458
+ std::optional<Match> UnitPattern::match(std::u32string_view text, size_t position) const {
459
+ for (auto& numbers : amounts_at(text, position, notation_)) {
460
+ size_t end = numbers.back().span.end;
461
+
462
+ auto unit_at = [&](size_t start, const Keys& units) -> std::optional<Match> {
463
+ for (auto unit : keys_at(text, start, units)) {
464
+ size_t unit_end = start + unit.size();
465
+ if (!is_clear_after(text, unit_end, U"'")) {
466
+ continue;
467
+ }
468
+ // A dot closing both the unit and the text stays with the text: 5 Min.
469
+ if (unit.ends_with(U'.') && is_sentence_end(text, unit_end)) {
470
+ unit_end--;
471
+ }
472
+ return Match{.kind = Kind::UNIT, .span = {position, unit_end}, .numbers = numbers, .unit = unit};
473
+ }
474
+ return std::nullopt;
475
+ };
476
+
477
+ // A unit after an optional space (5 kg, 5kg), or a single letter after a space (230 V)
478
+ for (size_t start : {skip_space(text, end), end}) {
479
+ if (auto unit = unit_at(start, notation_.units)) {
480
+ return unit;
481
+ }
482
+ }
483
+ if (end < text.size() && unicode::isspace(text[end])) {
484
+ if (auto unit = unit_at(end + 1, letters_)) {
485
+ return unit;
486
+ }
487
+ }
488
+ }
489
+ return std::nullopt;
490
+ }
491
+
492
+ CompoundPattern::CompoundPattern(const Notation& notation) : Pattern(Kind::COMPOUND), notation_(notation) {}
493
+
494
+ std::optional<Match> CompoundPattern::match(std::u32string_view text, size_t position) const {
495
+ if (!is_clear_before(text, position, kAmountBoundaries)) {
496
+ return std::nullopt;
497
+ }
498
+
499
+ for (auto& number : numbers_at(text, position, notation_.group_separators, notation_.decimal_separators)) {
500
+ size_t hyphen = number.span.end;
501
+ if (hyphen + 1 >= text.size() || text[hyphen] != U'-' || !unicode::is_latin(text[hyphen + 1])) {
502
+ continue;
503
+ }
504
+ size_t end = hyphen + 1;
505
+ while (end < text.size() && unicode::is_latin(text[end])) {
506
+ end++;
507
+ }
508
+ return Match{.kind = Kind::COMPOUND,
509
+ .span = {position, end},
510
+ .numbers = {number},
511
+ .suffix = text.substr(hyphen + 1, end - hyphen - 1)};
512
+ }
513
+ return std::nullopt;
514
+ }
515
+
516
+ std::optional<Match> CodePattern::match(std::u32string_view text, size_t position) const {
517
+ if (!is_clear_before(text, position, U"'.-")) {
518
+ return std::nullopt;
519
+ }
520
+
521
+ // A token of word characters, apostrophes, dots and hyphens, with both a digit and a Latin letter
522
+ size_t start = text[position] == U'.' ? position + 1 : position;
523
+ bool digit = false, letter = false;
524
+ for (size_t i = start;
525
+ i < text.size() && (unicode::isword(text[i]) || text[i] == U'\'' || text[i] == U'.' || text[i] == U'-'); ++i) {
526
+ digit |= unicode::isdigit(text[i]);
527
+ letter |= unicode::is_latin(text[i]);
528
+ }
529
+ if (!digit || !letter || start >= text.size() || !unicode::isword(text[start])) {
530
+ return std::nullopt;
531
+ }
532
+
533
+ // A dot only continues it before a digit: H.264
534
+ size_t end = start + 1;
535
+ while (end < text.size() && (unicode::isword(text[end]) || text[end] == U'\'' || text[end] == U'-' ||
536
+ (text[end] == U'.' && is_digit_after(text, end + 1)))) {
537
+ end++;
538
+ }
539
+ return Match{.kind = Kind::CODE, .span = {position, end}};
540
+ }
541
+
542
+ NumberPattern::NumberPattern(const Notation& notation) : Pattern(Kind::NUMBER), notation_(notation) {}
543
+
544
+ std::optional<Match> NumberPattern::match(std::u32string_view text, size_t position) const {
545
+ if (!is_clear_before(text, position, kNumberBoundaries)) {
546
+ return std::nullopt;
547
+ }
548
+
549
+ for (auto& number : numbers_at(text, position, notation_.group_separators, notation_.decimal_separators)) {
550
+ if (is_clear_after(text, number.span.end)) {
551
+ return Match{.kind = Kind::NUMBER, .span = number.span, .numbers = {number}};
552
+ }
553
+ }
554
+
555
+ // .5: a fraction without its integer part
556
+ if (text[position] == U'.' && strings::contains(notation_.decimal_separators, U'.') &&
557
+ is_digit_after(text, position + 1)) {
558
+ size_t end = digits_end(text, position + 1);
559
+ if (is_clear_after(text, end)) {
560
+ Number number{.span = {position, end},
561
+ .fraction = std::u32string(text.substr(position + 1, end - position - 1)),
562
+ .decimal_mark = U'.'};
563
+ return Match{.kind = Kind::NUMBER, .span = number.span, .numbers = {number}};
564
+ }
565
+ }
566
+ return std::nullopt;
567
+ }
568
+
569
+ // ---------------------------------
570
+ // ----- Configurable patterns -----
571
+ // ---------------------------------
572
+
573
+ DatePattern::DatePattern(DateFormat format) : Pattern(Kind::DATE), format_(std::move(format)) {}
574
+
575
+ std::optional<Match> DatePattern::match(std::u32string_view text, size_t position) const {
576
+ size_t day_end = digits_end(text, position);
577
+ if (!is_clear_before(text, position, format_.boundaries) || day_end == position || day_end > position + 2 ||
578
+ day_end >= text.size() || !strings::contains(format_.separators, text[day_end])) {
579
+ return std::nullopt;
580
+ }
581
+
582
+ char32_t separator = text[day_end];
583
+ size_t month = format_.spaced ? skip_space(text, day_end + 1) : day_end + 1;
584
+ size_t month_end = digits_end(text, month);
585
+ if (month_end == month || month_end > month + 2) {
586
+ return std::nullopt;
587
+ }
588
+
589
+ Match date{
590
+ .kind = Kind::DATE, .numbers = {digits_at(text, position), digits_at(text, month)}, .separator = separator};
591
+ int d = small_value(date.numbers[0].integer), m = small_value(date.numbers[1].integer);
592
+ if (d < 1 || d > 31 || m < 1 || m > 12) {
593
+ return std::nullopt;
594
+ }
595
+ date.span.begin = position;
596
+
597
+ // A closing separator always follows the month (1.5.), otherwise only before a year (12/05/2024)
598
+ bool closed = month_end < text.size() && text[month_end] == separator;
599
+ if (format_.closing_separator && !closed) {
600
+ return std::nullopt;
601
+ }
602
+
603
+ // With a year first, then (if allowed) without it
604
+ if (closed) {
605
+ size_t year = month_end + 1;
606
+ size_t length = digits_end(text, year) - year;
607
+ if (length == 4 || (length == 2 && format_.short_year)) {
608
+ Match dated = date;
609
+ dated.numbers.push_back(digits_at(text, year));
610
+ if (auto result = complete(text, dated, year + length)) {
611
+ return result;
612
+ }
613
+ }
614
+ }
615
+ if (format_.year_required) {
616
+ return std::nullopt;
617
+ }
618
+ return complete(text, date, format_.closing_separator ? month_end + 1 : month_end);
619
+ }
620
+
621
+ std::optional<Match> DatePattern::complete(std::u32string_view text, Match date, size_t position) const {
622
+ // An abbreviation after the year: 2024 r.
623
+ if (date.numbers.size() == 3) {
624
+ size_t suffix = skip_space(text, position);
625
+ for (auto key : keys_at(text, suffix, format_.suffixes)) {
626
+ size_t end = suffix + key.size();
627
+ if (is_clear_after(text, end) && !is_digit_after(text, end, format_.boundaries)) {
628
+ // A dot closing both the abbreviation and the sentence stays with the sentence
629
+ date.suffix = key;
630
+ date.span.end = key.ends_with(U'.') && is_sentence_end(text, end, true) ? end - 1 : end;
631
+ return date;
632
+ }
633
+ }
634
+ }
635
+
636
+ if (!is_clear_after(text, position) || is_digit_after(text, position, format_.boundaries)) {
637
+ return std::nullopt;
638
+ }
639
+ date.span.end = position;
640
+ return date;
641
+ }
642
+
643
+ std::optional<Match> InternationalPhonePattern::match(std::u32string_view text, size_t position) const {
644
+ if (text[position] != U'+' || !is_clear_before(text, position)) {
645
+ return std::nullopt;
646
+ }
647
+
648
+ // 1. The country code: one to three digits
649
+ Number country = digits_at(text, position + 1);
650
+ if (country.integer.empty() || country.integer.size() > 3) {
651
+ return std::nullopt;
652
+ }
653
+ Match phone{.kind = Kind::INTERNATIONAL_PHONE, .numbers = {country}};
654
+ size_t end = country.span.end, length = country.integer.size();
655
+
656
+ // 2. Groups of digits, each after a space, a hyphen or a dot, or in parentheses: +44 (0)20 7946-0958
657
+ for (bool closed = false; end < text.size();) {
658
+ size_t next = strings::contains(std::u32string_view(U" -."), text[end]) ? end + 1 : end;
659
+ bool parenthesized = next < text.size() && text[next] == U'(';
660
+ Number group = digits_at(text, next + parenthesized);
661
+ size_t group_end = group.span.end;
662
+ if (group.integer.empty() || (next == end && !parenthesized && !closed) ||
663
+ (parenthesized && (group_end >= text.size() || text[group_end] != U')'))) {
664
+ break;
665
+ }
666
+ phone.numbers.push_back(group);
667
+ end = group_end + parenthesized;
668
+ length += group.integer.size();
669
+ closed = parenthesized; // (0)20: a group may follow a closing parenthesis directly
670
+ }
671
+
672
+ // 3. A whole number: 8 to 15 digits, as numbering plans allow
673
+ if (phone.numbers.size() < 2 || length < 8 || length > 15 || !is_clear_after(text, end) ||
674
+ is_digit_after(text, end, U".,")) {
675
+ return std::nullopt;
676
+ }
677
+ phone.span = {position, end};
678
+ return phone;
679
+ }
680
+
681
+ PhonePattern::PhonePattern(PhoneFormat format) : Pattern(Kind::PHONE), format_(std::move(format)) {}
682
+
683
+ std::optional<Match> PhonePattern::match(std::u32string_view text, size_t position) const {
684
+ if (!is_clear_before(text, position, format_.boundaries)) {
685
+ return std::nullopt;
686
+ }
687
+
688
+ // With its optional prefix first (1-555-...), then without it
689
+ if (!format_.prefix.empty() && text.substr(position).starts_with(format_.prefix)) {
690
+ if (auto phone = groups_at(text, position, position + format_.prefix.size())) {
691
+ return phone;
692
+ }
693
+ }
694
+ return groups_at(text, position, position);
695
+ }
696
+
697
+ std::optional<Match> PhonePattern::groups_at(std::u32string_view text, size_t start, size_t position) const {
698
+ Match phone{.kind = Kind::PHONE};
699
+
700
+ // (555) 123-4567: the parenthesis opens the first group
701
+ bool parenthesized = format_.parenthesized;
702
+ if (parenthesized) {
703
+ if (position >= text.size() || text[position] != U'(') {
704
+ return std::nullopt;
705
+ }
706
+ position++;
707
+ }
708
+
709
+ for (size_t i = 0; i < format_.groups.size(); ++i) {
710
+ // The separator before a group, which a closing parenthesis replaces
711
+ bool after_parenthesis = i == 1 && parenthesized;
712
+ if (i > 0 && !after_parenthesis && position < text.size() &&
713
+ strings::contains(format_.separators, text[position])) {
714
+ phone.separator = phone.separator ? phone.separator : text[position];
715
+ position++;
716
+ } else if (i > 0 && !after_parenthesis && !format_.optional_separators) {
717
+ return std::nullopt;
718
+ }
719
+ if (i == 0 && format_.leading_zero && (position >= text.size() || text[position] != U'0')) {
720
+ return std::nullopt;
721
+ }
722
+
723
+ // Without separators, a run of digits splits into groups of their longest length
724
+ auto [shortest, longest] = format_.groups[i];
725
+ size_t length = digits_end(text, position) - position;
726
+ length = format_.optional_separators ? std::min(length, longest) : length;
727
+ if (length < shortest || length > longest) {
728
+ return std::nullopt;
729
+ }
730
+ phone.numbers.push_back(
731
+ {.span = {position, position + length}, .integer = std::u32string(text.substr(position, length))});
732
+ position += length;
733
+
734
+ if (i == 0 && parenthesized) {
735
+ if (position >= text.size() || text[position] != U')') {
736
+ return std::nullopt;
737
+ }
738
+ position = skip_space(text, position + 1);
739
+ }
740
+ }
741
+
742
+ if (!is_clear_after(text, position, format_.closing) || is_digit_after(text, position, U".,")) {
743
+ return std::nullopt;
744
+ }
745
+ phone.span = {start, position};
746
+ return phone;
747
+ }
748
+
749
+ SuffixPattern::SuffixPattern(Kind kind, Keys suffixes, SuffixFormat format)
750
+ : Pattern(kind), suffixes_(std::move(suffixes)), format_(std::move(format)) {}
751
+
752
+ std::optional<Match> SuffixPattern::match(std::u32string_view text, size_t position) const {
753
+ if (!is_clear_before(text, position, format_.boundaries)) {
754
+ return std::nullopt;
755
+ }
756
+
757
+ // '90s: an apostrophe before the digits
758
+ size_t digits = format_.apostrophes && text[position] == U'\'' ? position + 1 : position;
759
+ size_t end = digits_end(text, digits);
760
+ if (end == digits) {
761
+ return std::nullopt;
762
+ }
763
+
764
+ // 90's, 1 º: an apostrophe or a space before the suffix
765
+ size_t suffix = end;
766
+ if (format_.apostrophes && suffix < text.size() && text[suffix] == U'\'') {
767
+ suffix++;
768
+ }
769
+ if (format_.space) {
770
+ suffix = skip_space(text, suffix);
771
+ }
772
+
773
+ for (auto key : keys_at(text, suffix, suffixes_)) {
774
+ size_t suffix_end = suffix + key.size();
775
+ if (format_.word_end && !is_clear_after(text, suffix_end)) {
776
+ continue;
777
+ }
778
+ // A dot closing both the suffix and the sentence stays with the sentence: 2020 r.
779
+ if (format_.sentence_dot && key.ends_with(U'.') && is_sentence_end(text, suffix_end, true)) {
780
+ suffix_end--;
781
+ }
782
+ return Match{.kind = kind(), .span = {position, suffix_end}, .numbers = {digits_at(text, digits)}, .suffix = key};
783
+ }
784
+ return std::nullopt;
785
+ }
786
+
787
+ DotOrdinalPattern::DotOrdinalPattern(bool before_punctuation, bool sentence_dot)
788
+ : Pattern(Kind::ORDINAL), before_punctuation_(before_punctuation), sentence_dot_(sentence_dot) {}
789
+
790
+ std::optional<Match> DotOrdinalPattern::match(std::u32string_view text, size_t position) const {
791
+ size_t dot = digits_end(text, position);
792
+ if (!is_clear_before(text, position, kNumberBoundaries) || dot == position || dot >= text.size() ||
793
+ text[dot] != U'.') {
794
+ return std::nullopt;
795
+ }
796
+
797
+ // Followed by a word, or (optionally) by a comma or the end of the text
798
+ size_t next = dot + 1;
799
+ while (next < text.size() && unicode::isspace(text[next])) {
800
+ next++;
801
+ }
802
+ bool word = next > dot + 1 && next < text.size();
803
+ bool punctuation = before_punctuation_ && (dot + 1 == text.size() || text[dot + 1] == U',');
804
+ if (!word && !punctuation) {
805
+ return std::nullopt;
806
+ }
807
+ size_t end = sentence_dot_ && is_sentence_end(text, dot + 1, true) ? dot : dot + 1;
808
+ return Match{.kind = Kind::ORDINAL, .span = {position, end}, .numbers = {digits_at(text, position)}, .suffix = U"."};
809
+ }
810
+
811
+ } // namespace phonemis::processor::num2word