plurimath 0.11.6 → 0.11.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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/lib/plurimath/asciimath/utility.rb +50 -0
  3. data/lib/plurimath/asciimath.rb +1 -0
  4. data/lib/plurimath/formatter/numbers/mathml_renderer.rb +15 -15
  5. data/lib/plurimath/formatter/numbers/omml_renderer.rb +19 -19
  6. data/lib/plurimath/html/utility.rb +18 -0
  7. data/lib/plurimath/html.rb +1 -0
  8. data/lib/plurimath/latex/utility.rb +118 -0
  9. data/lib/plurimath/latex.rb +1 -0
  10. data/lib/plurimath/math/core.rb +12 -12
  11. data/lib/plurimath/math/formula.rb +10 -10
  12. data/lib/plurimath/math/function/abs.rb +3 -3
  13. data/lib/plurimath/math/function/bar.rb +14 -14
  14. data/lib/plurimath/math/function/base.rb +15 -7
  15. data/lib/plurimath/math/function/binary_function.rb +7 -7
  16. data/lib/plurimath/math/function/cancel.rb +2 -2
  17. data/lib/plurimath/math/function/ceil.rb +1 -1
  18. data/lib/plurimath/math/function/color.rb +2 -2
  19. data/lib/plurimath/math/function/ddot.rb +3 -3
  20. data/lib/plurimath/math/function/dot.rb +8 -8
  21. data/lib/plurimath/math/function/fenced.rb +8 -8
  22. data/lib/plurimath/math/function/floor.rb +1 -1
  23. data/lib/plurimath/math/function/font_style/bold.rb +2 -2
  24. data/lib/plurimath/math/function/font_style/double_struck.rb +2 -2
  25. data/lib/plurimath/math/function/font_style/fraktur.rb +2 -2
  26. data/lib/plurimath/math/function/font_style/italic.rb +2 -2
  27. data/lib/plurimath/math/function/font_style/monospace.rb +2 -2
  28. data/lib/plurimath/math/function/font_style/normal.rb +2 -2
  29. data/lib/plurimath/math/function/font_style/sans-serif.rb +2 -2
  30. data/lib/plurimath/math/function/font_style/script.rb +2 -2
  31. data/lib/plurimath/math/function/font_style.rb +16 -15
  32. data/lib/plurimath/math/function/frac.rb +7 -7
  33. data/lib/plurimath/math/function/hat.rb +8 -8
  34. data/lib/plurimath/math/function/inf.rb +2 -2
  35. data/lib/plurimath/math/function/int.rb +8 -8
  36. data/lib/plurimath/math/function/left.rb +5 -5
  37. data/lib/plurimath/math/function/lim.rb +3 -3
  38. data/lib/plurimath/math/function/limits.rb +2 -2
  39. data/lib/plurimath/math/function/linebreak.rb +2 -2
  40. data/lib/plurimath/math/function/log.rb +10 -10
  41. data/lib/plurimath/math/function/longdiv.rb +1 -1
  42. data/lib/plurimath/math/function/menclose.rb +2 -2
  43. data/lib/plurimath/math/function/merror.rb +2 -2
  44. data/lib/plurimath/math/function/mod.rb +1 -1
  45. data/lib/plurimath/math/function/mpadded.rb +2 -2
  46. data/lib/plurimath/math/function/ms.rb +2 -2
  47. data/lib/plurimath/math/function/msgroup.rb +2 -2
  48. data/lib/plurimath/math/function/multiscript.rb +2 -2
  49. data/lib/plurimath/math/function/nary.rb +15 -15
  50. data/lib/plurimath/math/function/none.rb +1 -1
  51. data/lib/plurimath/math/function/norm.rb +1 -1
  52. data/lib/plurimath/math/function/obrace.rb +8 -8
  53. data/lib/plurimath/math/function/oint.rb +6 -6
  54. data/lib/plurimath/math/function/over.rb +5 -5
  55. data/lib/plurimath/math/function/overleftrightarrow.rb +7 -7
  56. data/lib/plurimath/math/function/overset.rb +5 -5
  57. data/lib/plurimath/math/function/phantom.rb +9 -9
  58. data/lib/plurimath/math/function/power.rb +5 -5
  59. data/lib/plurimath/math/function/power_base.rb +5 -5
  60. data/lib/plurimath/math/function/prod.rb +4 -4
  61. data/lib/plurimath/math/function/right.rb +5 -5
  62. data/lib/plurimath/math/function/root.rb +7 -7
  63. data/lib/plurimath/math/function/rule.rb +2 -2
  64. data/lib/plurimath/math/function/scarries.rb +1 -1
  65. data/lib/plurimath/math/function/semantics.rb +4 -4
  66. data/lib/plurimath/math/function/sqrt.rb +7 -7
  67. data/lib/plurimath/math/function/stackrel.rb +5 -5
  68. data/lib/plurimath/math/function/substack.rb +1 -1
  69. data/lib/plurimath/math/function/sum.rb +4 -4
  70. data/lib/plurimath/math/function/sup.rb +1 -1
  71. data/lib/plurimath/math/function/table/array.rb +1 -1
  72. data/lib/plurimath/math/function/table/bmatrix.rb +2 -2
  73. data/lib/plurimath/math/function/table/matrix.rb +2 -2
  74. data/lib/plurimath/math/function/table.rb +29 -29
  75. data/lib/plurimath/math/function/td.rb +3 -3
  76. data/lib/plurimath/math/function/ternary_function.rb +13 -13
  77. data/lib/plurimath/math/function/text.rb +4 -4
  78. data/lib/plurimath/math/function/tilde.rb +6 -6
  79. data/lib/plurimath/math/function/tr.rb +4 -4
  80. data/lib/plurimath/math/function/ubrace.rb +1 -1
  81. data/lib/plurimath/math/function/ul.rb +9 -9
  82. data/lib/plurimath/math/function/unary_function.rb +14 -14
  83. data/lib/plurimath/math/function/underover.rb +1 -1
  84. data/lib/plurimath/math/function/underset.rb +12 -12
  85. data/lib/plurimath/math/function/unitsml.rb +1 -1
  86. data/lib/plurimath/math/function/vec.rb +7 -7
  87. data/lib/plurimath/math/model_helper.rb +103 -0
  88. data/lib/plurimath/math/number.rb +2 -2
  89. data/lib/plurimath/math/symbols/symbol.rb +2 -2
  90. data/lib/plurimath/math/symbols/tilde.rb +1 -1
  91. data/lib/plurimath/math.rb +1 -0
  92. data/lib/plurimath/mathml/constants.rb +29 -155
  93. data/lib/plurimath/mathml/formula_transformation.rb +2 -5
  94. data/lib/plurimath/mathml/translator.rb +6 -0
  95. data/lib/plurimath/mathml/utility.rb +53 -0
  96. data/lib/plurimath/mathml.rb +1 -0
  97. data/lib/plurimath/omml/formula_transformation.rb +5 -5
  98. data/lib/plurimath/omml/translator.rb +8 -5
  99. data/lib/plurimath/omml/utility.rb +109 -0
  100. data/lib/plurimath/omml.rb +1 -0
  101. data/lib/plurimath/unicode_math/transform.rb +5 -1
  102. data/lib/plurimath/unicode_math/utility.rb +256 -0
  103. data/lib/plurimath/unicode_math.rb +1 -0
  104. data/lib/plurimath/utility/intent_encoding.rb +4 -4
  105. data/lib/plurimath/utility.rb +6 -815
  106. data/lib/plurimath/version.rb +1 -1
  107. data/lib/plurimath/xml_engine/oga/dumper.rb +8 -1
  108. data/lib/plurimath/xml_engine/ox_engine.rb +4 -1
  109. data/lib/plurimath/xml_helper.rb +47 -0
  110. data/lib/plurimath.rb +1 -0
  111. metadata +10 -2
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plurimath
4
+ module Math
5
+ # Display/serialization support helpers for the Math node classes,
6
+ # extracted from Plurimath::Utility (macro refactor). Deliberately a
7
+ # standalone class — NOT a Utility subclass — so it does not shadow
8
+ # bareword `Utility` references inside math files.
9
+ class ModelHelper
10
+ TEXT_CLASSES = %w[
11
+ symbol
12
+ number
13
+ text
14
+ ].freeze
15
+
16
+ class << self
17
+ def validate_left_right(fields = [])
18
+ fields.each do |field|
19
+ if field.is_a?(Math::Formula) && field.value.first.is_a?(Math::Function::Left)
20
+ field.left_right_wrapper = true
21
+ end
22
+ end
23
+ end
24
+
25
+ def validate_math_zone(object, lang:, intent: false, options: nil)
26
+ return false unless object
27
+
28
+ if object.is_a?(Math::Formula)
29
+ filter_math_zone_values(object.value, lang: lang, intent: intent,
30
+ options: options).find do |value|
31
+ !(value.is_a?(Math::Function::Text) || value.is_a?(Math::Symbols::Symbol))
32
+ end
33
+ else
34
+ !(TEXT_CLASSES.include?(object.class_name) || object.is_a?(Math::Symbols::Symbol))
35
+ end
36
+ end
37
+
38
+ def filter_math_zone_values(value, lang:, intent: false, options: nil)
39
+ return [] if value.nil? || value.empty?
40
+
41
+ new_arr = []
42
+ temp_array = []
43
+ skip_index = nil
44
+ value.each_with_index do |obj, index|
45
+ object = obj.dup
46
+ next if index == skip_index
47
+ if TEXT_CLASSES.include?(object.class_name) || math_display_text_objects(object)
48
+ next temp_array << (if object.is_a?(Math::Symbols::Symbol)
49
+ symbol_to_text(
50
+ object, lang: lang, intent: intent, options: options
51
+ )
52
+ else
53
+ object.value
54
+ end)
55
+ end
56
+
57
+ if temp_array.any?
58
+ new_arr << Math::Function::Text.new(temp_array.join(" "),
59
+ lang: lang)
60
+ end
61
+ temp_array = []
62
+ new_arr << object
63
+ end
64
+ if temp_array.any?
65
+ new_arr << Math::Function::Text.new(temp_array.join(" "),
66
+ lang: lang)
67
+ end
68
+ new_arr
69
+ end
70
+
71
+ def notations_to_mask(notations)
72
+ mask = notations.split.map do |notation|
73
+ Utility::MASK_CLASSES.key(notation)
74
+ end
75
+ mask.compact.sum ^ 15
76
+ end
77
+
78
+ private
79
+
80
+ def symbol_to_text(symbol, lang:, options:, intent: false)
81
+ case lang
82
+ when :asciimath
83
+ symbol.to_asciimath(options: options)
84
+ when :latex
85
+ symbol.to_latex(options: options)
86
+ when :mathml
87
+ symbol.to_mathml_without_math_tag(intent,
88
+ options: options).nodes.first
89
+ when :omml
90
+ symbol.to_omml_without_math_tag(true, options: options)
91
+ when :unicodemath
92
+ symbol.to_unicodemath(options: options)
93
+ end
94
+ end
95
+
96
+ def math_display_text_objects(object)
97
+ class_names = ["plus", "minus", "circ", "equal", "symbol"].freeze
98
+ class_names.include?(object.class_name)
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -60,7 +60,7 @@ module Plurimath
60
60
 
61
61
  def insert_t_tag(_, options:)
62
62
  [
63
- Utility.ox_element("r", namespace: "m") << t_tag(options: options),
63
+ XmlHelper.ox_element("r", namespace: "m") << t_tag(options: options),
64
64
  ]
65
65
  end
66
66
 
@@ -69,7 +69,7 @@ module Plurimath
69
69
  end
70
70
 
71
71
  def t_tag(options:)
72
- Utility.ox_element("t",
72
+ XmlHelper.ox_element("t",
73
73
  namespace: "m") << formatted_value(options)
74
74
  end
75
75
 
@@ -83,7 +83,7 @@ module Plurimath
83
83
  # TODO: remove this condition once to word rendering issue is resolved, plurimath/plurimath/pull/328
84
84
  return if value == "&#x2062;"
85
85
 
86
- [Utility.ox_element("r", namespace: "m") << t_tag(options: options)]
86
+ [XmlHelper.ox_element("r", namespace: "m") << t_tag(options: options)]
87
87
  end
88
88
 
89
89
  def tag_name
@@ -234,7 +234,7 @@ options:)
234
234
  end
235
235
 
236
236
  def t_element
237
- Utility.ox_element("t", namespace: "m")
237
+ XmlHelper.ox_element("t", namespace: "m")
238
238
  end
239
239
 
240
240
  def explicit_checks(unicode)
@@ -3,7 +3,7 @@ module Plurimath
3
3
  module Symbols
4
4
  class Tilde < Symbol
5
5
  INPUT = {
6
- unicodemath: [["~"]],
6
+ unicodemath: [["&#x303;", "~"]],
7
7
  asciimath: [["~"]],
8
8
  mathml: ["~"],
9
9
  latex: [["~"]],
@@ -5,6 +5,7 @@ module Plurimath
5
5
  autoload :Core, "#{__dir__}/math/core"
6
6
  autoload :Evaluation, "#{__dir__}/math/evaluation"
7
7
  autoload :Formula, "#{__dir__}/math/formula"
8
+ autoload :ModelHelper, "#{__dir__}/math/model_helper"
8
9
  autoload :Function, "#{__dir__}/math/function"
9
10
  autoload :InvalidTypeError, "#{__dir__}/errors/invalid_type_error"
10
11
  autoload :Number, "#{__dir__}/math/number"
@@ -29,7 +29,7 @@ module Plurimath
29
29
  "&#x3C0;": "pi",
30
30
  "&#x3a0;": "Pi",
31
31
  "&#x3c1;": "rho",
32
- "&#x3c2;": "beta",
32
+ "&#x3c2;": "varsigma",
33
33
  "&#x3c3;": "sigma",
34
34
  "&#x3a3;": "Sigma",
35
35
  "&#x3c4;": "tau",
@@ -41,7 +41,7 @@ module Plurimath
41
41
  "&#x3c8;": "psi",
42
42
  "&#x3a8;": "Psi",
43
43
  "&#x3c9;": "omega",
44
- "&#x3a9;": "omega",
44
+ "&#x3a9;": "Omega",
45
45
  "&#x22c5;": "cdot",
46
46
  "&#x2219;": "*",
47
47
  "&#x2e;": ".",
@@ -155,6 +155,16 @@ module Plurimath
155
155
  "¯": "bar",
156
156
  _: "ul",
157
157
  }.freeze
158
+
159
+ # Accent characters promoted to their function class ONLY in an accent
160
+ # position (MathML <mover>, OMML <m:acc>). Kept separate from
161
+ # UNICODE_SYMBOLS because Text#value= consumes that map as a global
162
+ # substitution table, so an entry there would also rewrite the character
163
+ # inside ordinary text runs and bare tokens.
164
+ CONTEXTUAL_ACCENT_FUNCTIONS = {
165
+ "&#x303;": "tilde",
166
+ }.freeze
167
+
158
168
  SYMBOLS = {
159
169
  "|": "|",
160
170
  "/": "//",
@@ -174,22 +184,25 @@ module Plurimath
174
184
  "+": "+",
175
185
  "-": "-",
176
186
  }.freeze
177
- # Over/under accent decorations. In MathML these are only ever expressed
178
- # as <mover>/<munder> with a base (recovered from their diacritic
179
- # CHARACTER), never as a bare <mi>/<mo> identifier word. So a bare token
180
- # whose text is one of these names is a literal identifier (e.g. the unit
181
- # "bar"), not the accent. See unitsml/unitsdb#123.
182
- ACCENT_WORDS = %w[
183
- bar overline ul hat vec tilde dot ddot obrace ubrace overleftrightarrow
187
+ # Named operators that have no dedicated MathML/OMML representation — no
188
+ # unicode character and no structural element — so a bare <mi>/<mo>/<m:t>
189
+ # word (e.g. <mi>sin</mi>, <m:t>min</m:t>) is their only written form and
190
+ # resolves to the function class. Every other CLASSES entry has a proper
191
+ # form — a diacritic character (bar/hat/vec via <mover>), a unicode symbol
192
+ # (sum/prod/int via &#x2211; etc.), or a structural element (frac via
193
+ # <mfrac>/<m:f>, sqrt via <msqrt>) — so its bare word is a literal
194
+ # identifier, not the function. See PR #450 (which introduced this for accents).
195
+ NAMED_FUNCTION_WORDS = %w[
196
+ arccos arcsin arctan sin cos tan sec csc cot sinh cosh tanh coth sech
197
+ csch log ln exp det dim gcd lcm glb lub max min mod
184
198
  ].freeze
185
199
 
186
- # True when +string+ is a bare word naming an over/under accent. Such
187
- # tokens are literal identifiers in MathML (the accent itself is a
188
- # diacritic character inside <mover>/<munder>), so callers must not
189
- # promote them to accent operators. AsciiMath/LaTeX word forms
190
- # (bar(x), \bar{x}) are parsed elsewhere and never reach this predicate.
191
- def self.accent_word?(string)
192
- ACCENT_WORDS.include?(string&.strip)
200
+ # True when +string+ is a bare word naming an operator that has no other
201
+ # MathML/OMML representation, so it legitimately resolves to its function
202
+ # class. Words for constructs that DO have a character/structural form
203
+ # (accents, sum/prod/int, frac/sqrt/...) return false and stay literal.
204
+ def self.named_function_word?(string)
205
+ NAMED_FUNCTION_WORDS.include?(string&.strip)
193
206
  end
194
207
 
195
208
  CLASSES = %w[
@@ -255,145 +268,6 @@ module Plurimath
255
268
  ul
256
269
  ln
257
270
  ].freeze
258
- OPERATORS = [
259
- "&#x00a0;&#x00a0;&#x00a0;&#x00a0;",
260
- "&#x00a0;&#x00a0;",
261
- "&#x002a;",
262
- "&#x003c;",
263
- "&#x003e;",
264
- "&#x00a0;",
265
- "&#x00ac;",
266
- "&#x00b1;",
267
- "&#x00d7;",
268
- "&#x00f7;",
269
- "&#x0393;",
270
- "&#x0394;",
271
- "&#x0398;",
272
- "&#x039b;",
273
- "&#x039e;",
274
- "&#x03a0;",
275
- "&#x03a3;",
276
- "&#x03a9;",
277
- "&#x2026;",
278
- "&#x2032;",
279
- "&#x2102;",
280
- "&#x2115;",
281
- "&#x211a;",
282
- "&#x211d;",
283
- "&#x2124;",
284
- "&#x2135;",
285
- "&#x2190;",
286
- "&#x2191;",
287
- "&#x2192;",
288
- "&#x2192;",
289
- "&#x2193;",
290
- "&#x2194;",
291
- "&#x21a0;",
292
- "&#x21a3;",
293
- "&#x21a6;",
294
- "&#x21d0;",
295
- "&#x21d2;",
296
- "&#x21d2;",
297
- "&#x21d4;",
298
- "&#x21d4;",
299
- "&#x2200;",
300
- "&#x2202;",
301
- "&#x2203;",
302
- "&#x2205;",
303
- "&#x2207;",
304
- "&#x2208;",
305
- "&#x2209;",
306
- "&#x220f;",
307
- "&#x2211;",
308
- "&#x2212;",
309
- "&#x2213;",
310
- "&#x221d;",
311
- "&#x221e;",
312
- "&#x2220;",
313
- "&#x2227;",
314
- "&#x2228;",
315
- "&#x2229;",
316
- "&#x222a;",
317
- "&#x222b;",
318
- "&#x222e;",
319
- "&#x2234;",
320
- "&#x2235;",
321
- "&#x223c;",
322
- "&#x2245;",
323
- "&#x2248;",
324
- "&#x2254;",
325
- "&#x2260;",
326
- "&#x2261;",
327
- "&#x2264;",
328
- "&#x2265;",
329
- "&#x226a;",
330
- "&#x226b;",
331
- "&#x227a;",
332
- "&#x227b;",
333
- "&#x2282;",
334
- "&#x2283;",
335
- "&#x2286;",
336
- "&#x2287;",
337
- "&#x2295;",
338
- "&#x2297;",
339
- "&#x2299;",
340
- "&#x22a2;",
341
- "&#x22a4;",
342
- "&#x22a5;",
343
- "&#x22a8;",
344
- "&#x22c0;",
345
- "&#x22c1;",
346
- "&#x22c2;",
347
- "&#x22c3;",
348
- "&#x22c4;",
349
- "&#x22c5;",
350
- "&#x22c6;",
351
- "&#x22c8;",
352
- "&#x22c9;",
353
- "&#x22ca;",
354
- "&#x22ee;",
355
- "&#x22ef;",
356
- "&#x22f1;",
357
- "&#x2308;",
358
- "&#x2309;",
359
- "&#x230a;",
360
- "&#x230b;",
361
- "&#x2322;",
362
- "&#x25a1;",
363
- "&#x25b3;",
364
- "&#x26ac;",
365
- "&#x2916;",
366
- "&#x2aaf;",
367
- "&#x2ab0;",
368
- "&#x2b;",
369
- "&#x3d;",
370
- "&#x3a;",
371
- "&#x3c;",
372
- "&#xd7;",
373
- "&#xb1;",
374
- "&#x26;",
375
- "&#x3b;",
376
- "&#x3e;",
377
- "&#x7c;",
378
- "lim",
379
- "max",
380
- "min",
381
- "\\",
382
- "//",
383
- "/",
384
- "if",
385
- "^",
386
- "_",
387
- "+",
388
- "/",
389
- "=",
390
- ":",
391
- ",",
392
- ")",
393
- "(",
394
- "[",
395
- "]",
396
- ].freeze
397
271
  SUPPORTED_FONT_STYLES = {
398
272
  "sans-serif-bold-italic": Math::Function::FontStyle::SansSerifBoldItalic,
399
273
  "sans-serif-italic": Math::Function::FontStyle::SansSerifItalic,
@@ -28,7 +28,7 @@ module Plurimath
28
28
  end
29
29
 
30
30
  def mathml_symbol(value)
31
- Plurimath::Utility.mathml_unary_classes([value], lang: :mathml)
31
+ Utility.resolve_token(value)
32
32
  end
33
33
 
34
34
  # Alignment markers affect MathML layout only; the old parser dropped them
@@ -423,10 +423,7 @@ module Plurimath
423
423
  end
424
424
 
425
425
  def mathml_symbol_to_class(symbol)
426
- Plurimath::Utility.mathml_unary_classes(
427
- Array(symbol),
428
- lang: :mathml,
429
- )
426
+ Utility.resolve_token(symbol)
430
427
  end
431
428
 
432
429
  def value_is_ternary_or_nary?(value)
@@ -122,6 +122,12 @@ module Plurimath
122
122
  children = content_children(mover)
123
123
  base = filter_child(mml_to_plurimath(children[0]))
124
124
  overscript = filter_child(mml_to_plurimath(children[1]))
125
+ # An accent character that has no function mapping arrives as a plain
126
+ # symbol; promote it here, where it is unambiguously an accent.
127
+ if overscript.instance_of?(Plurimath::Math::Symbols::Symbol)
128
+ overscript =
129
+ Utility.contextual_accent_from_token(overscript.value) || overscript
130
+ end
125
131
  options = {}
126
132
  options[:accent] = true if truthy_mathml_bool?(mover.accent)
127
133
 
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plurimath
4
+ class Mathml
5
+ module FunctionTokenResolver
6
+ # Promotes an accent character to its function class. Callers must only
7
+ # use this from an accent position; elsewhere the character has to stay a
8
+ # plain symbol/text. Decode-then-encode normalizes both call sites: MathML
9
+ # passes an already-encoded "&#x303;", OMML passes the raw U+0303.
10
+ def contextual_accent_from_token(value)
11
+ return nil if value.nil?
12
+
13
+ entity = string_to_html_entity(html_entity_to_unicode(value.to_s))
14
+ function_name =
15
+ Mathml::Constants::CONTEXTUAL_ACCENT_FUNCTIONS[entity.strip.to_sym]
16
+ get_class(function_name).new if function_name
17
+ end
18
+
19
+ private
20
+
21
+ def function_from_token(value)
22
+ return nil if value.nil?
23
+
24
+ string = value.to_s
25
+ word = string.strip
26
+ unicode = string_to_html_entity(string)
27
+ symbol = Mathml::Constants::UNICODE_SYMBOLS[unicode.strip.to_sym]&.strip
28
+ function_name =
29
+ if Mathml::Constants::CLASSES.include?(symbol)
30
+ symbol
31
+ elsif Mathml::Constants::CLASSES.include?(word) &&
32
+ Mathml::Constants.named_function_word?(word)
33
+ word
34
+ end
35
+
36
+ get_class(function_name).new if function_name
37
+ end
38
+ end
39
+
40
+ class Utility < Plurimath::Utility
41
+ extend FunctionTokenResolver
42
+
43
+ class << self
44
+ def resolve_token(value)
45
+ return nil if value.nil?
46
+
47
+ function_from_token(value) ||
48
+ symbols_class(string_to_html_entity(value.to_s), lang: :mathml)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -6,6 +6,7 @@ module Plurimath
6
6
  autoload :FormulaTransformation, "#{__dir__}/mathml/formula_transformation"
7
7
  autoload :Parser, "#{__dir__}/mathml/parser"
8
8
  autoload :Translator, "#{__dir__}/mathml/translator"
9
+ autoload :Utility, "#{__dir__}/mathml/utility"
9
10
 
10
11
  attr_accessor :text
11
12
 
@@ -62,7 +62,7 @@ module Plurimath
62
62
  return Math::Function::Text.new("", lang: :omml) if value == ""
63
63
  return nil if value == "\u200B"
64
64
 
65
- Utility.mathml_unary_classes([value], omml: true, lang: :omml)
65
+ Utility.resolve_text_token(value)
66
66
  end
67
67
 
68
68
  def omml_argument_value(node)
@@ -182,7 +182,7 @@ module Plurimath
182
182
  end
183
183
 
184
184
  def nary_operator(character)
185
- character && Utility.mathml_unary_classes([character], lang: :omml)
185
+ character && Utility.resolve_symbol_token(character)
186
186
  end
187
187
 
188
188
  def nary_type(properties)
@@ -228,13 +228,13 @@ module Plurimath
228
228
  delimiter_symbol(character.val)
229
229
  end
230
230
 
231
- def accent_value(value)
232
- accent = value.first
231
+ def accent_value(accent, argument)
233
232
  unless accent.is_a?(Math::Function::UnaryFunction)
234
- return Math::Function::Overset.new(accent, value.last,
233
+ return Math::Function::Overset.new(accent, argument,
235
234
  { accent: true })
236
235
  end
237
236
 
237
+ accent.parameter_one ||= argument
238
238
  accent.attributes = { accent: true }
239
239
  accent
240
240
  end
@@ -169,7 +169,6 @@ module Plurimath
169
169
  omml_argument_value(node.f_name),
170
170
  omml_argument_value(node.e),
171
171
  ],
172
- lang: :omml,
173
172
  ),
174
173
  )
175
174
  end
@@ -204,10 +203,14 @@ module Plurimath
204
203
  def accent_to_plurimath(node)
205
204
  pr = first_omml_child(node.acc_pr)
206
205
  chr = first_omml_child(pr&.chr)&.val
207
- accent = chr ? Utility.mathml_unary_classes([chr], lang: :omml) : Math::Function::Hat.new
208
- value = [accent, omml_argument_value(node.e)]
209
- Utility.unary_function_classes(value, lang: :omml)
210
- accent_value(value)
206
+ accent =
207
+ if chr
208
+ Utility.contextual_accent_from_token(chr) ||
209
+ Utility.resolve_symbol_token(chr)
210
+ else
211
+ Math::Function::Hat.new
212
+ end
213
+ accent_value(accent, omml_argument_value(node.e))
211
214
  end
212
215
 
213
216
  def bar_to_plurimath(node)
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "plurimath/mathml/utility"
4
+
5
+ module Plurimath
6
+ class Omml
7
+ # Omml-specific helper extracted from Plurimath::Utility (code-quality
8
+ # refactor). Subclasses Utility so bareword `Utility.<generic>` calls inside
9
+ # Omml files keep resolving here and inherit the generic helpers.
10
+ class Utility < Plurimath::Utility
11
+ extend Plurimath::Mathml::FunctionTokenResolver
12
+
13
+ class << self
14
+ def valid_class(object)
15
+ text = object.extract_class_name_from_text
16
+ (object.extractable? && Asciimath::Constants::SUB_SUP_CLASSES.include?(text)) ||
17
+ Latex::Constants::SYMBOLS[text&.to_sym] == :power_base
18
+ end
19
+
20
+ def resolve_text_token(value)
21
+ return nil if value.nil?
22
+
23
+ function_from_token(value) || text_token(value)
24
+ end
25
+
26
+ def resolve_symbol_token(value)
27
+ return nil if value.nil?
28
+
29
+ function_from_token(value) ||
30
+ symbols_class(string_to_html_entity(value.to_s), lang: :omml)
31
+ end
32
+
33
+ def populate_function_classes(mrow = [])
34
+ flatten_mrow = mrow.flatten.compact
35
+ unary_function_classes(flatten_mrow)
36
+ binary_function_classes(flatten_mrow)
37
+ ternary_function_classes(flatten_mrow)
38
+ flatten_mrow
39
+ end
40
+
41
+ private
42
+
43
+ def binary_function_classes(mrow)
44
+ mrow.each_with_index do |object, ind|
45
+ mrow[ind] = resolve_symbol_token(object) if object.is_a?(String)
46
+ object = mrow[ind]
47
+ next unless object.is_a?(Math::Function::Mod)
48
+
49
+ object.parameter_one = mrow.delete_at(ind - 1) unless ind.zero?
50
+ object.parameter_two = mrow.delete_at(ind)
51
+ end
52
+ end
53
+
54
+ def unary_function_classes(mrow)
55
+ unary_class = Math::Function::UnaryFunction
56
+ if mrow.any?(String) || mrow.any?(unary_class)
57
+ mrow.each_with_index do |object, ind|
58
+ if object.is_a?(String)
59
+ mrow[ind] = resolve_symbol_token(object)
60
+ object = mrow[ind]
61
+ end
62
+ next unless object.is_a?(unary_class)
63
+ next if object.is_a?(Math::Function::Text)
64
+ next if object.parameter_one || mrow[ind + 1].nil?
65
+ next unless ind.zero?
66
+
67
+ object.parameter_one = mrow.delete_at(ind + 1)
68
+ end
69
+ end
70
+ end
71
+
72
+ def ternary_function_classes(mrow)
73
+ ternary_class = Math::Function::TernaryFunction
74
+ if mrow.any?(ternary_class) && mrow.length > 1
75
+ mrow.each_with_index do |object, ind|
76
+ if object.is_a?(ternary_class)
77
+ next if [Math::Function::Fenced, Math::Function::Multiscript].include?(object.class)
78
+ next unless object.parameter_one || object.parameter_two
79
+ next if object.parameter_three
80
+
81
+ object.parameter_three = filter_values(mrow.delete_at(ind + 1))
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ def text_token(text)
88
+ text = filter_values(text) unless text.is_a?(String)
89
+ return text if text.is_a?(Math::Core)
90
+
91
+ if text.scan(/[[:digit:]]/).length == text.length
92
+ Math::Number.new(text)
93
+ elsif text.match?(/[a-zA-Z]/)
94
+ Math::Function::Text.new(text, lang: :omml)
95
+ else
96
+ text = string_to_html_entity(text)
97
+ .gsub("&#x26;", "&")
98
+ .gsub("&#x3c;", "<")
99
+ .gsub("&#x27;", "'")
100
+ .gsub("&#xa0;", "\u00A0")
101
+ .gsub("&#x3e;", ">")
102
+ .gsub("&#xa;", "\n")
103
+ symbols_class(text, lang: :omml)
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -22,6 +22,7 @@ module Plurimath
22
22
  autoload :FormulaTransformation, "#{__dir__}/omml/formula_transformation"
23
23
  autoload :Parser, "#{__dir__}/omml/parser"
24
24
  autoload :Translator, "#{__dir__}/omml/translator"
25
+ autoload :Utility, "#{__dir__}/omml/utility"
25
26
  autoload :UnsupportedNodeError,
26
27
  "#{__dir__}/errors/omml/unsupported_node_error"
27
28
 
@@ -116,7 +116,11 @@ module Plurimath
116
116
  end
117
117
 
118
118
  rule(prefixed_prime: simple(:prime)) do
119
- Utility.updated_primes(prime)
119
+ # Normalize the slash-prefixed name (\prime, \pprime, ...) to its entity
120
+ # so the enclosing prime_accent_symbols handling resolves it like the
121
+ # direct entity forms. Resolving to a node here instead would be dropped
122
+ # by the outer updated_primes re-scan (which only matches entities).
123
+ Constants::PREFIXED_PRIMES[prime.to_s.to_sym] || prime
120
124
  end
121
125
 
122
126
  rule(unary_functions: simple(:unary)) do