plurimath 0.11.12 → 0.11.13

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 (170) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-tests.yml +7 -0
  3. data/.github/workflows/shared-testsuite.yml +24 -0
  4. data/.gitmodules +3 -0
  5. data/.rspec-opal +3 -0
  6. data/Gemfile +18 -0
  7. data/README.adoc +297 -3
  8. data/docs/catalog.adoc +73 -0
  9. data/docs/uncertainty-asciimath-tree.md +92 -0
  10. data/docs/uncertainty.adoc +522 -0
  11. data/lib/plurimath/asciimath/parse.rb +43 -226
  12. data/lib/plurimath/asciimath/parser.rb +58 -1
  13. data/lib/plurimath/asciimath/slice_adapter.rb +138 -0
  14. data/lib/plurimath/asciimath/transform.rb +218 -0
  15. data/lib/plurimath/asciimath.rb +1 -0
  16. data/lib/plurimath/catalog/entry.rb +109 -0
  17. data/lib/plurimath/catalog/validation.rb +109 -0
  18. data/lib/plurimath/catalog.rb +96 -0
  19. data/lib/plurimath/cli/convert.rb +40 -0
  20. data/lib/plurimath/cli/helpers.rb +49 -0
  21. data/lib/plurimath/cli/render.rb +64 -0
  22. data/lib/plurimath/cli.rb +55 -57
  23. data/lib/plurimath/documentation.rb +99 -0
  24. data/lib/plurimath/errors/ambiguous_decimal_error.rb +19 -0
  25. data/lib/plurimath/errors/invalid_uncertainty.rb +18 -0
  26. data/lib/plurimath/errors/missing_dependency_error.rb +23 -0
  27. data/lib/plurimath/errors/rendering_failed.rb +17 -0
  28. data/lib/plurimath/errors/rendering_unavailable.rb +25 -0
  29. data/lib/plurimath/errors/unsupported_render_format.rb +18 -0
  30. data/lib/plurimath/errors/unsupported_semantic_tree_error.rb +12 -0
  31. data/lib/plurimath/errors.rb +5 -0
  32. data/lib/plurimath/math/evaluation/evaluator.rb +48 -0
  33. data/lib/plurimath/math/evaluation/expression_parser.rb +29 -20
  34. data/lib/plurimath/math/formula/class_tree.rb +92 -0
  35. data/lib/plurimath/math/formula.rb +43 -2
  36. data/lib/plurimath/math/function/abs.rb +6 -0
  37. data/lib/plurimath/math/function/arccos.rb +6 -0
  38. data/lib/plurimath/math/function/arcsin.rb +6 -0
  39. data/lib/plurimath/math/function/arctan.rb +6 -0
  40. data/lib/plurimath/math/function/arg.rb +20 -0
  41. data/lib/plurimath/math/function/bar.rb +6 -0
  42. data/lib/plurimath/math/function/base.rb +6 -0
  43. data/lib/plurimath/math/function/binary_function.rb +4 -0
  44. data/lib/plurimath/math/function/cancel.rb +6 -0
  45. data/lib/plurimath/math/function/ceil.rb +6 -0
  46. data/lib/plurimath/math/function/color.rb +12 -0
  47. data/lib/plurimath/math/function/cos.rb +6 -0
  48. data/lib/plurimath/math/function/cosh.rb +6 -0
  49. data/lib/plurimath/math/function/cot.rb +6 -0
  50. data/lib/plurimath/math/function/coth.rb +6 -0
  51. data/lib/plurimath/math/function/csc.rb +6 -0
  52. data/lib/plurimath/math/function/csch.rb +6 -0
  53. data/lib/plurimath/math/function/ddot.rb +6 -0
  54. data/lib/plurimath/math/function/deg.rb +6 -0
  55. data/lib/plurimath/math/function/det.rb +6 -0
  56. data/lib/plurimath/math/function/dim.rb +6 -0
  57. data/lib/plurimath/math/function/dot.rb +6 -0
  58. data/lib/plurimath/math/function/exp.rb +6 -0
  59. data/lib/plurimath/math/function/fenced.rb +6 -0
  60. data/lib/plurimath/math/function/floor.rb +6 -0
  61. data/lib/plurimath/math/function/font_style/bold-fraktur.rb +6 -0
  62. data/lib/plurimath/math/function/font_style/bold-italic.rb +6 -0
  63. data/lib/plurimath/math/function/font_style/bold-sans-serif.rb +6 -0
  64. data/lib/plurimath/math/function/font_style/bold-script.rb +6 -0
  65. data/lib/plurimath/math/function/font_style/bold.rb +6 -0
  66. data/lib/plurimath/math/function/font_style/double_struck.rb +6 -0
  67. data/lib/plurimath/math/function/font_style/fraktur.rb +6 -0
  68. data/lib/plurimath/math/function/font_style/italic.rb +6 -0
  69. data/lib/plurimath/math/function/font_style/monospace.rb +6 -0
  70. data/lib/plurimath/math/function/font_style/normal.rb +6 -0
  71. data/lib/plurimath/math/function/font_style/sans-serif-bold-italic.rb +6 -0
  72. data/lib/plurimath/math/function/font_style/sans-serif-italic.rb +6 -0
  73. data/lib/plurimath/math/function/font_style/sans-serif.rb +6 -0
  74. data/lib/plurimath/math/function/font_style/script.rb +6 -0
  75. data/lib/plurimath/math/function/font_style.rb +4 -0
  76. data/lib/plurimath/math/function/frac.rb +6 -0
  77. data/lib/plurimath/math/function/gcd.rb +6 -0
  78. data/lib/plurimath/math/function/glb.rb +12 -0
  79. data/lib/plurimath/math/function/hat.rb +6 -0
  80. data/lib/plurimath/math/function/hom.rb +6 -0
  81. data/lib/plurimath/math/function/inf.rb +6 -0
  82. data/lib/plurimath/math/function/int.rb +7 -1
  83. data/lib/plurimath/math/function/intent.rb +35 -0
  84. data/lib/plurimath/math/function/ker.rb +6 -0
  85. data/lib/plurimath/math/function/lcm.rb +12 -0
  86. data/lib/plurimath/math/function/lg.rb +6 -0
  87. data/lib/plurimath/math/function/lim.rb +6 -0
  88. data/lib/plurimath/math/function/liminf.rb +6 -0
  89. data/lib/plurimath/math/function/limits.rb +6 -0
  90. data/lib/plurimath/math/function/limsup.rb +6 -0
  91. data/lib/plurimath/math/function/ln.rb +6 -0
  92. data/lib/plurimath/math/function/log.rb +6 -0
  93. data/lib/plurimath/math/function/longdiv.rb +6 -0
  94. data/lib/plurimath/math/function/lub.rb +6 -0
  95. data/lib/plurimath/math/function/max.rb +6 -0
  96. data/lib/plurimath/math/function/mbox.rb +6 -0
  97. data/lib/plurimath/math/function/menclose.rb +19 -0
  98. data/lib/plurimath/math/function/merror.rb +22 -0
  99. data/lib/plurimath/math/function/min.rb +6 -0
  100. data/lib/plurimath/math/function/mlabeledtr.rb +23 -0
  101. data/lib/plurimath/math/function/mod.rb +6 -0
  102. data/lib/plurimath/math/function/msgroup.rb +20 -0
  103. data/lib/plurimath/math/function/multiscript.rb +6 -0
  104. data/lib/plurimath/math/function/nary.rb +11 -0
  105. data/lib/plurimath/math/function/norm.rb +6 -0
  106. data/lib/plurimath/math/function/obrace.rb +6 -0
  107. data/lib/plurimath/math/function/oint.rb +7 -1
  108. data/lib/plurimath/math/function/over.rb +6 -0
  109. data/lib/plurimath/math/function/overleftrightarrow.rb +6 -0
  110. data/lib/plurimath/math/function/overset.rb +6 -0
  111. data/lib/plurimath/math/function/phantom.rb +6 -0
  112. data/lib/plurimath/math/function/power.rb +6 -0
  113. data/lib/plurimath/math/function/power_base.rb +6 -0
  114. data/lib/plurimath/math/function/prod.rb +7 -1
  115. data/lib/plurimath/math/function/right.rb +1 -1
  116. data/lib/plurimath/math/function/root.rb +6 -0
  117. data/lib/plurimath/math/function/rule.rb +20 -0
  118. data/lib/plurimath/math/function/scarries.rb +20 -0
  119. data/lib/plurimath/math/function/sec.rb +6 -0
  120. data/lib/plurimath/math/function/sech.rb +6 -0
  121. data/lib/plurimath/math/function/semantics.rb +20 -0
  122. data/lib/plurimath/math/function/sin.rb +6 -0
  123. data/lib/plurimath/math/function/sinh.rb +6 -0
  124. data/lib/plurimath/math/function/sqrt.rb +6 -0
  125. data/lib/plurimath/math/function/stackrel.rb +6 -0
  126. data/lib/plurimath/math/function/substack.rb +6 -0
  127. data/lib/plurimath/math/function/sum.rb +7 -1
  128. data/lib/plurimath/math/function/sup.rb +6 -0
  129. data/lib/plurimath/math/function/table/align.rb +6 -0
  130. data/lib/plurimath/math/function/table/array.rb +6 -0
  131. data/lib/plurimath/math/function/table/bmatrix.rb +6 -0
  132. data/lib/plurimath/math/function/table/cases.rb +6 -0
  133. data/lib/plurimath/math/function/table/eqarray.rb +6 -0
  134. data/lib/plurimath/math/function/table/matrix.rb +6 -0
  135. data/lib/plurimath/math/function/table/multline.rb +6 -0
  136. data/lib/plurimath/math/function/table/pmatrix.rb +6 -0
  137. data/lib/plurimath/math/function/table/split.rb +6 -0
  138. data/lib/plurimath/math/function/table/vmatrix.rb +6 -0
  139. data/lib/plurimath/math/function/table.rb +10 -0
  140. data/lib/plurimath/math/function/tan.rb +6 -0
  141. data/lib/plurimath/math/function/tanh.rb +6 -0
  142. data/lib/plurimath/math/function/ternary_function.rb +4 -0
  143. data/lib/plurimath/math/function/text.rb +6 -0
  144. data/lib/plurimath/math/function/tilde.rb +6 -0
  145. data/lib/plurimath/math/function/ubrace.rb +6 -0
  146. data/lib/plurimath/math/function/ul.rb +6 -0
  147. data/lib/plurimath/math/function/unary_function.rb +4 -0
  148. data/lib/plurimath/math/function/uncertainty.rb +655 -0
  149. data/lib/plurimath/math/function/underover.rb +6 -0
  150. data/lib/plurimath/math/function/underset.rb +6 -0
  151. data/lib/plurimath/math/function/vec.rb +6 -0
  152. data/lib/plurimath/math/function.rb +1 -0
  153. data/lib/plurimath/math/renderer.rb +95 -0
  154. data/lib/plurimath/math/semantic_tree.rb +331 -0
  155. data/lib/plurimath/math/symbols/symbol.rb +2 -0
  156. data/lib/plurimath/math.rb +26 -0
  157. data/lib/plurimath/mathml/translator.rb +32 -0
  158. data/lib/plurimath/mathml/uncertainty_marker.rb +118 -0
  159. data/lib/plurimath/omml/translator.rb +68 -0
  160. data/lib/plurimath/omml/utility.rb +9 -1
  161. data/lib/plurimath/symbol_documentation.rb +57 -0
  162. data/lib/plurimath/unicode_math/parsing_rules/common_rules.rb +2 -1
  163. data/lib/plurimath/unicode_math/parsing_rules/masked.rb +3 -0
  164. data/lib/plurimath/unicode_math/transform.rb +64 -3
  165. data/lib/plurimath/unicode_math/utility.rb +38 -0
  166. data/lib/plurimath/version.rb +1 -1
  167. data/lib/plurimath.rb +3 -0
  168. data/plurimath.gemspec +3 -1
  169. data/scripts/replay_asciimath_corpus.rb +611 -0
  170. metadata +30 -4
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../mathml/uncertainty_marker"
4
+
3
5
  module Plurimath
4
6
  class Omml
5
7
  class Translator
@@ -261,9 +263,75 @@ module Plurimath
261
263
  end
262
264
 
263
265
  def box_to_plurimath(node)
266
+ children = ordered_omml_children(first_omml_child(node.e))
267
+ marker = uncertainty_marker_text(children.first)
268
+ return box_to_uncertainty(marker, children.drop(1)) if marker
269
+
264
270
  omml_argument_value(node.e)
265
271
  end
266
272
 
273
+ # An m:box whose first child is a hidden m:phant holding an
274
+ # uncertainty marker (plurimath#493). The marker is authoritative; the
275
+ # visible content is regenerated on output, so only the inner m:box
276
+ # holding the unit is read.
277
+ def box_to_uncertainty(marker, visible)
278
+ Mathml::UncertaintyMarker.build(marker) do
279
+ units = visible.select { |child| omml_class?(child, "CTBox") }
280
+ if units.length > 1
281
+ raise Errors::InvalidUncertainty.new(
282
+ "marker #{marker.inspect}: more than one m:box could hold the unit",
283
+ )
284
+ end
285
+
286
+ unit = units.first && box_to_plurimath(units.first)
287
+ unit.nil? || unit.is_a?(Math::Formula) ? unit : Math::Formula.new([unit])
288
+ end
289
+ end
290
+
291
+ def uncertainty_marker_text(phantom)
292
+ return unless omml_class?(phantom, "CTPhant") && hidden_phantom?(phantom)
293
+
294
+ text = phantom_run_text(phantom)
295
+ text if Mathml::UncertaintyMarker.marker?(text)
296
+ end
297
+
298
+ # The text of a phantom made only of runs, or nil. A phantom whose
299
+ # leading runs spell a marker but which also holds anything other than
300
+ # runs is a malformed marker: it raises rather than being read as
301
+ # ordinary content.
302
+ def phantom_run_text(phantom)
303
+ children = ordered_omml_children(first_omml_child(phantom.e))
304
+ runs = children.take_while { |child| omml_class?(child, "CTR") }
305
+ text = runs.map { |run| run_text(run) }.join
306
+ return text if runs.any? && runs.length == children.length
307
+
308
+ reject_mixed_phantom(text)
309
+ end
310
+
311
+ def reject_mixed_phantom(text)
312
+ return unless Mathml::UncertaintyMarker.marker?(text)
313
+
314
+ raise Errors::InvalidUncertainty.new(
315
+ "marker #{text.inspect}: the hidden m:phant must hold only m:r runs",
316
+ )
317
+ end
318
+
319
+ def hidden_phantom?(phantom)
320
+ show = first_omml_child(first_omml_child(phantom.phant_pr)&.show)
321
+ show && %w[0 false off].include?(show.val.to_s)
322
+ end
323
+
324
+ def run_text(run)
325
+ ordered_omml_children(run).filter_map do |child|
326
+ child.content.to_s if omml_class?(child, "CTText")
327
+ end.join
328
+ end
329
+
330
+ def omml_class?(node, name)
331
+ !node.nil? && !node.is_a?(String) &&
332
+ Lutaml::Model::Utils.base_class_name(node.class.name) == name
333
+ end
334
+
267
335
  def word_run_properties_to_plurimath(node)
268
336
  word_run_font(node)
269
337
  end
@@ -10,6 +10,14 @@ module Plurimath
10
10
  class Utility < Plurimath::Utility
11
11
  extend Plurimath::Mathml::FunctionTokenResolver
12
12
 
13
+ # A run whose whole text is a number: digits with an optional
14
+ # decimal part. OMML has no <mn>/<mi> split, so the run's text
15
+ # decides. A mixed run such as "2x" stays text, "1,5" stays a
16
+ # symbol (list or decimal is ambiguous), and a signed run such as
17
+ # "-1" stays a symbol: the shared testsuite expects that for
18
+ # superscripts like sin^-1, and to_omml writes the sign as its own run.
19
+ NUMBER_RUN = /\A[[:digit:]]+(?:\.[[:digit:]]+)?\z/
20
+
13
21
  class << self
14
22
  def valid_class(object)
15
23
  text = object.extract_class_name_from_text
@@ -88,7 +96,7 @@ module Plurimath
88
96
  text = filter_values(text) unless text.is_a?(String)
89
97
  return text if text.is_a?(Math::Core)
90
98
 
91
- if text.scan(/[[:digit:]]/).length == text.length
99
+ if text.match?(NUMBER_RUN)
92
100
  Math::Number.new(text)
93
101
  elsif text.match?(/[a-zA-Z]/)
94
102
  Math::Function::Text.new(text, lang: :omml)
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plurimath
4
+ # Per-symbol metadata for the catalog (see Plurimath::Catalog), extended into
5
+ # Symbols::Symbol. Symbols are documented differently from functions: a symbol
6
+ # is an atomic glyph with no arguments, so it needs no hand-authored
7
+ # description, reference, or example — its four renderings ARE its
8
+ # documentation. Everything here is derived, so every concrete symbol is
9
+ # catalogued without per-class declarations. (Functions, by contrast, each
10
+ # explain themselves via the separate Plurimath::Documentation.)
11
+ module SymbolDocumentation
12
+ # Slug for the /symbols/<name>/ page and YAML filename: Alpha -> "alpha".
13
+ def catalog_name
14
+ name.split("::").last.downcase
15
+ end
16
+
17
+ def catalog_type
18
+ :symbol
19
+ end
20
+
21
+ def catalog_kind
22
+ :symbol
23
+ end
24
+
25
+ def catalog_arity
26
+ nil
27
+ end
28
+
29
+ # The glyph itself, wrapped in a Formula for rendering.
30
+ def example_formula
31
+ Math::Formula.new([new])
32
+ end
33
+
34
+ # Anonymous subclasses have no stable catalog name and are excluded.
35
+ # Catalogued when the symbol is a concrete glyph — i.e. not one of the
36
+ # abstract bases (Symbol, Paren), which exist only to be subclassed. Checked
37
+ # by identity rather than by rendering: cheaper across the ~1,459 symbols and
38
+ # independent of which formats a given glyph happens to serialize to.
39
+ def documented?
40
+ !name.nil? && ![Math::Symbols::Symbol, Math::Symbols::Paren].include?(self)
41
+ end
42
+
43
+ # The YAML-ready entry: the glyph's name, type, and rendering in each format.
44
+ # Leaner than a function entry — a symbol carries no description or reference.
45
+ def catalog_entry
46
+ limitations = if const_defined?(:CATALOG_FORMAT_LIMITATIONS, false)
47
+ const_get(:CATALOG_FORMAT_LIMITATIONS, false)
48
+ else
49
+ {}
50
+ end
51
+ Catalog::Entry.build(
52
+ name: catalog_name, type: catalog_type, kind: catalog_kind,
53
+ arity: catalog_arity, formula: example_formula, limitations: limitations
54
+ )
55
+ end
56
+ end
57
+ end
@@ -56,7 +56,8 @@ module Plurimath
56
56
  (str("\\") >> custom_fonts.as(:unicoded_font_class) >> str("H").as(:symbol)) |
57
57
  (str("\\") >> str("mitBbb").as(:unicoded_font_class) >> match(/D|d|e|i|j/).as(:symbol)) |
58
58
  (op_fonts >> match["A-Za-z"].as(:symbol)) |
59
- (op_alphanumeric_fonts >> (match["A-Za-z"].as(:symbol) | match("[0-9]").as(:number)))
59
+ (op_alphanumeric_fonts >> (match["A-Za-z"].as(:symbol) | match("[0-9]").as(:number))) |
60
+ ((op_fonts | op_alphanumeric_fonts) >> parsing_text)
60
61
  end
61
62
 
62
63
  rule(:unary_arg_functions) do
@@ -35,8 +35,11 @@ module Plurimath
35
35
  rule(:root_symbols) do
36
36
  str("&#x24ad;") | str("&#x221a;") | str("\\root") | str("\\surd")
37
37
  end
38
+ # The middle form takes content of several terms, e.g. ⓐ(unit \mupkg);
39
+ # `expression` alone stops after the first term (plurimath#493).
38
40
  rule(:arg_function) do
39
41
  (str("&#x24d0;").as(:arg) >> str("(") >> a_ascii.as(:arg_arguments).maybe >> space? >> expression.as(:first_value).maybe >> str(")")) |
42
+ (str("&#x24d0;").as(:arg) >> str("(") >> a_ascii.as(:arg_arguments) >> space? >> masked_recursive_value.as(:first_value) >> space? >> str(")")) |
40
43
  (str("&#x24d0;").as(:arg) >> a_ascii.as(:arg_arguments).maybe >> space? >> expression.as(:first_value).maybe)
41
44
  end
42
45
 
@@ -233,6 +233,13 @@ module Plurimath
233
233
  Utility.symbols_class((unicode || symbol), lang: :unicodemath)
234
234
  end
235
235
 
236
+ # \mup"g" is how a font over text is written (e.g. a unit read from
237
+ # OMML, plurimath#493).
238
+ rule(font_class: simple(:fonts),
239
+ text: simple(:text)) do
240
+ Utility::FONT_STYLES[fonts.to_sym].new(Math::Function::Text.new(text))
241
+ end
242
+
236
243
  rule(font_class: simple(:fonts),
237
244
  symbol: simple(:symbol)) do
238
245
  Utility::FONT_STYLES[fonts.to_sym].new(
@@ -530,6 +537,18 @@ module Plurimath
530
537
  [Utility.symbols_class(symbol, lang: :unicodemath), exp]
531
538
  end
532
539
 
540
+ rule(combined_symbols: simple(:combined_symbols),
541
+ expr: simple(:expr)) do
542
+ symbol = Constants::COMBINING_SYMBOLS[combined_symbols.to_sym] || combined_symbols
543
+ [Utility.symbols_class(symbol, lang: :unicodemath), expr]
544
+ end
545
+
546
+ rule(combined_symbols: simple(:combined_symbols),
547
+ expr: sequence(:expr)) do
548
+ symbol = Constants::COMBINING_SYMBOLS[combined_symbols.to_sym] || combined_symbols
549
+ [Utility.symbols_class(symbol, lang: :unicodemath)] + expr
550
+ end
551
+
533
552
  rule(operator: simple(:operator),
534
553
  sub_script: simple(:sub_script)) do
535
554
  [Utility.symbols_class(operator, lang: :unicodemath), sub_script]
@@ -960,7 +979,7 @@ module Plurimath
960
979
  rule(intent: simple(:intent),
961
980
  intent_expr: simple(:expr)) do
962
981
  intent_string = expr.parameter_two.shift
963
- Math::Function::Intent.new(
982
+ Utility.intent_or_uncertainty(
964
983
  Utility.filter_values(expr.parameter_two),
965
984
  intent_string,
966
985
  )
@@ -2218,12 +2237,30 @@ module Plurimath
2218
2237
  )
2219
2238
  end
2220
2239
 
2240
+ # Any other ⓐ shape has no Arg reading; fail here rather than leave
2241
+ # the raw parse tree inside the Formula. Parslet tries later rules
2242
+ # first, so these catch-alls must stay above the Arg rules below.
2243
+ rule(arg: simple(:arg)) { Utility.unsupported_arg! }
2244
+ rule(arg: simple(:arg), arg_arguments: simple(:args)) { Utility.unsupported_arg! }
2245
+ rule(arg: simple(:arg), first_value: subtree(:first_value)) { Utility.unsupported_arg! }
2246
+ rule(arg: simple(:arg),
2247
+ arg_arguments: simple(:args),
2248
+ first_value: subtree(:first_value)) { Utility.unsupported_arg! }
2249
+
2221
2250
  rule(arg: simple(:arg),
2222
2251
  arg_arguments: simple(:args),
2223
2252
  first_value: simple(:first_value)) do
2224
2253
  Math::Function::Arg.new(first_value, args)
2225
2254
  end
2226
2255
 
2256
+ # ⓐ(unit \mupkg) reads its content as several nodes; keep them together
2257
+ # so the argument (e.g. an uncertainty unit, plurimath#493) is not lost.
2258
+ rule(arg: simple(:arg),
2259
+ arg_arguments: simple(:args),
2260
+ first_value: sequence(:first_value)) do
2261
+ Math::Function::Arg.new(Math::Formula.new(first_value), args)
2262
+ end
2263
+
2227
2264
  rule(whole: simple(:whole),
2228
2265
  decimal: simple(:decimal),
2229
2266
  fractional: simple(:fractional)) do
@@ -3585,6 +3622,30 @@ module Plurimath
3585
3622
  )
3586
3623
  end
3587
3624
 
3625
+ rule(open_paren: simple(:open_paren),
3626
+ text: simple(:text),
3627
+ operand: sequence(:operand),
3628
+ exp: simple(:exp),
3629
+ close_paren: simple(:close_paren)) do
3630
+ Math::Function::Fenced.new(
3631
+ open_paren.is_a?(Slice) ? Utility.symbols_class(open_paren, lang: :unicodemath) : open_paren,
3632
+ [Math::Function::Text.new(text)] + operand + [exp],
3633
+ close_paren.is_a?(Slice) ? Utility.symbols_class(close_paren, lang: :unicodemath) : close_paren,
3634
+ )
3635
+ end
3636
+
3637
+ rule(open_paren: simple(:open_paren),
3638
+ text: simple(:text),
3639
+ operand: sequence(:operand),
3640
+ exp: sequence(:exp),
3641
+ close_paren: simple(:close_paren)) do
3642
+ Math::Function::Fenced.new(
3643
+ open_paren.is_a?(Slice) ? Utility.symbols_class(open_paren, lang: :unicodemath) : open_paren,
3644
+ [Math::Function::Text.new(text)] + operand + exp,
3645
+ close_paren.is_a?(Slice) ? Utility.symbols_class(close_paren, lang: :unicodemath) : close_paren,
3646
+ )
3647
+ end
3648
+
3588
3649
  rule(nary_class: simple(:nary_class),
3589
3650
  mask: simple(:mask),
3590
3651
  sub: simple(:sub),
@@ -3871,7 +3932,7 @@ module Plurimath
3871
3932
  intent_arguments: simple(:args),
3872
3933
  first_value: simple(:value),
3873
3934
  close_paren: simple(:close_paren)) do
3874
- Math::Function::Intent.new(value, args)
3935
+ Utility.intent_or_uncertainty(value, args)
3875
3936
  end
3876
3937
 
3877
3938
  rule(intent: simple(:intent),
@@ -3879,7 +3940,7 @@ module Plurimath
3879
3940
  intent_arguments: simple(:args),
3880
3941
  first_value: sequence(:value),
3881
3942
  close_paren: simple(:close_paren)) do
3882
- Math::Function::Intent.new(Utility.filter_values(value), args)
3943
+ Utility.intent_or_uncertainty(Utility.filter_values(value), args)
3883
3944
  end
3884
3945
 
3885
3946
  rule(paren_open_prefix: simple(:open_paren),
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../mathml/uncertainty_marker"
4
+
3
5
  module Plurimath
4
6
  class UnicodeMath
5
7
  # UnicodeMath-specific helpers extracted from Plurimath::Utility (code-quality
@@ -7,6 +9,42 @@ module Plurimath
7
9
  # UnicodeMath files keep resolving here and inherit the generic helpers.
8
10
  class Utility < Plurimath::Utility
9
11
  class << self
12
+ # ⓘ("marker" content) is a measurement uncertainty (plurimath#493)
13
+ # when the marker uses one of its concept names; any other intent
14
+ # stays a Function::Intent. The marker is authoritative, so only the
15
+ # ⓐ(unit ...) argument of the content is read.
16
+ def intent_or_uncertainty(content, intent)
17
+ marker = intent.is_a?(Math::Function::Text) ? intent.parameter_one : intent.to_s
18
+ unless Mathml::UncertaintyMarker.marker?(marker)
19
+ return Math::Function::Intent.new(content, intent)
20
+ end
21
+
22
+ Mathml::UncertaintyMarker.build(marker) do
23
+ uncertainty_unit(content, marker)
24
+ end
25
+ end
26
+
27
+ def uncertainty_unit(content, marker)
28
+ values = content.is_a?(Math::Formula) ? content.value : Array(content)
29
+ units = values.select do |value|
30
+ value.is_a?(Math::Function::Arg) &&
31
+ value.parameter_two.is_a?(Math::Symbols::Symbol) &&
32
+ value.parameter_two.value == "unit"
33
+ end
34
+ if units.length > 1
35
+ raise Errors::InvalidUncertainty.new(
36
+ "marker #{marker.inspect}: more than one ⓐ(unit ...) argument",
37
+ )
38
+ end
39
+
40
+ unit = units.first&.parameter_one
41
+ unit.nil? || unit.is_a?(Math::Formula) ? unit : Math::Formula.new([unit])
42
+ end
43
+
44
+ def unsupported_arg!
45
+ raise ArgumentError, "ⓐ needs a name and content, as in ⓐ(name content)"
46
+ end
47
+
10
48
  def unicode_accents(accents, lang: :unicodemath)
11
49
  if accents.is_a?(Math::Function::BinaryFunction)
12
50
  accents
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Plurimath
4
- VERSION = "0.11.12"
4
+ VERSION = "0.11.13"
5
5
  end
data/lib/plurimath.rb CHANGED
@@ -8,9 +8,12 @@ require "plurimath/xml_engine"
8
8
  module Plurimath
9
9
  autoload :Asciimath, "plurimath/asciimath"
10
10
  autoload :BaseNumberPrefix, "plurimath/base_number_prefix"
11
+ autoload :Catalog, "plurimath/catalog"
11
12
  autoload :Cli, "plurimath/cli" unless RUBY_ENGINE == "opal"
12
13
  autoload :Configuration, "plurimath/configuration"
13
14
  autoload :Deprecation, "plurimath/deprecation"
15
+ autoload :Documentation, "plurimath/documentation"
16
+ autoload :SymbolDocumentation, "plurimath/symbol_documentation"
14
17
  autoload :Error, "plurimath/errors/error"
15
18
  autoload :ConfigurationError, "plurimath/errors/configuration_error"
16
19
  autoload :DeprecationError, "plurimath/errors/deprecation_error"
data/plurimath.gemspec CHANGED
@@ -27,9 +27,11 @@ Gem::Specification.new do |spec|
27
27
  spec.bindir = "exe"
28
28
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
29
  spec.require_paths = ["lib"]
30
+ # amath comes from the Gemfile (GitHub) until it is published; then:
31
+ # spec.add_dependency "amath", "~> 0.1"
30
32
  spec.add_dependency "bigdecimal"
31
33
  spec.add_dependency "htmlentities"
32
- spec.add_dependency "lutaml-model", "~> 0.8.65"
34
+ spec.add_dependency "lutaml-model", "~> 0.8.155"
33
35
  spec.add_dependency "mml", "~> 2.4.2"
34
36
  spec.add_dependency "omml", "~> 0.2.5"
35
37
  spec.add_dependency "ostruct"