plurimath 0.11.17 → 0.11.18

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 (189) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-tests.yml +2 -2
  3. data/.github/workflows/docs.yml +26 -0
  4. data/.github/workflows/gum-reference.yml +48 -0
  5. data/.github/workflows/performance-ci-legs.yml +54 -0
  6. data/.github/workflows/performance.yml +99 -0
  7. data/.github/workflows/roundtrip-cases.yml +33 -0
  8. data/.rubocop_todo.yml +0 -138
  9. data/Gemfile +13 -0
  10. data/Rakefile +2 -0
  11. data/docs/asciimath.adoc +578 -0
  12. data/docs/core-model-contract.adoc +748 -0
  13. data/docs/gum-evaluation.adoc +368 -0
  14. data/docs/latex.adoc +335 -0
  15. data/docs/mathml.adoc +638 -0
  16. data/docs/native-evaluation-request.md +28 -0
  17. data/docs/numeric-request.md +19 -0
  18. data/docs/omml.adoc +589 -0
  19. data/docs/performance-contract.adoc +279 -0
  20. data/docs/pma.adoc +949 -0
  21. data/docs/quantity-request.md +19 -0
  22. data/docs/uncertainty-asciimath-tree.md +2 -3
  23. data/docs/uncertainty.adoc +401 -425
  24. data/docs/unicodemath.adoc +453 -0
  25. data/docs/unitsml.adoc +414 -0
  26. data/lib/plurimath/asciimath/constants.rb +0 -98
  27. data/lib/plurimath/asciimath/mapper/rules.rb +1400 -0
  28. data/lib/plurimath/asciimath/mapper.rb +148 -0
  29. data/lib/plurimath/asciimath/parse.rb +23 -16
  30. data/lib/plurimath/asciimath/parser.rb +25 -11
  31. data/lib/plurimath/asciimath/slice_adapter.rb +1 -1
  32. data/lib/plurimath/asciimath.rb +1 -1
  33. data/lib/plurimath/catalog.rb +4 -5
  34. data/lib/plurimath/cli/convert.rb +3 -2
  35. data/lib/plurimath/cli.rb +2 -1
  36. data/lib/plurimath/configuration.rb +3 -0
  37. data/lib/plurimath/evaluation_request/quantity.rb +254 -0
  38. data/lib/plurimath/evaluation_request.rb +160 -0
  39. data/lib/plurimath/latex/constants.rb +0 -84
  40. data/lib/plurimath/latex/mapper.rb +418 -0
  41. data/lib/plurimath/latex/parse.rb +28 -288
  42. data/lib/plurimath/latex/parser.rb +49 -4
  43. data/lib/plurimath/latex.rb +1 -1
  44. data/lib/plurimath/math/core.rb +266 -1
  45. data/lib/plurimath/math/evaluation/accuracy.rb +62 -0
  46. data/lib/plurimath/math/evaluation/calibration.rb +85 -0
  47. data/lib/plurimath/math/evaluation/compiled_polynomial.rb +99 -0
  48. data/lib/plurimath/math/evaluation/compiled_rational.rb +131 -0
  49. data/lib/plurimath/math/evaluation/conformity.rb +133 -0
  50. data/lib/plurimath/math/evaluation/correlated_normal_sampler.rb +74 -0
  51. data/lib/plurimath/math/evaluation/covariance.rb +173 -0
  52. data/lib/plurimath/math/evaluation/distribution_sampler.rb +126 -0
  53. data/lib/plurimath/math/evaluation/evaluator.rb +236 -22
  54. data/lib/plurimath/math/evaluation/grouped_statistics.rb +56 -0
  55. data/lib/plurimath/math/evaluation/gum.rb +410 -0
  56. data/lib/plurimath/math/evaluation/implicit_model.rb +66 -0
  57. data/lib/plurimath/math/evaluation/monte_carlo.rb +198 -0
  58. data/lib/plurimath/math/evaluation/quantity_evaluator.rb +127 -0
  59. data/lib/plurimath/math/evaluation/result.rb +45 -0
  60. data/lib/plurimath/math/evaluation/statistics.rb +102 -0
  61. data/lib/plurimath/math/evaluation/taylor.rb +162 -0
  62. data/lib/plurimath/math/evaluation/wichmann_hill.rb +40 -0
  63. data/lib/plurimath/math/evaluation.rb +22 -0
  64. data/lib/plurimath/math/formula/mrow.rb +6 -1
  65. data/lib/plurimath/math/formula.rb +22 -4
  66. data/lib/plurimath/math/function/abs.rb +3 -1
  67. data/lib/plurimath/math/function/arccos.rb +1 -1
  68. data/lib/plurimath/math/function/arcsin.rb +1 -1
  69. data/lib/plurimath/math/function/arctan.rb +1 -1
  70. data/lib/plurimath/math/function/bar.rb +2 -1
  71. data/lib/plurimath/math/function/base.rb +2 -1
  72. data/lib/plurimath/math/function/binary_function.rb +1 -1
  73. data/lib/plurimath/math/function/ceil.rb +3 -1
  74. data/lib/plurimath/math/function/color.rb +2 -1
  75. data/lib/plurimath/math/function/cos.rb +1 -1
  76. data/lib/plurimath/math/function/cosh.rb +1 -1
  77. data/lib/plurimath/math/function/cot.rb +1 -1
  78. data/lib/plurimath/math/function/coth.rb +1 -1
  79. data/lib/plurimath/math/function/csc.rb +1 -1
  80. data/lib/plurimath/math/function/csch.rb +1 -1
  81. data/lib/plurimath/math/function/ddot.rb +2 -1
  82. data/lib/plurimath/math/function/dot.rb +2 -1
  83. data/lib/plurimath/math/function/exp.rb +1 -1
  84. data/lib/plurimath/math/function/fenced.rb +5 -3
  85. data/lib/plurimath/math/function/floor.rb +3 -1
  86. data/lib/plurimath/math/function/font_style/bold.rb +1 -1
  87. data/lib/plurimath/math/function/font_style/double_struck.rb +1 -1
  88. data/lib/plurimath/math/function/font_style/fraktur.rb +1 -1
  89. data/lib/plurimath/math/function/font_style/italic.rb +1 -1
  90. data/lib/plurimath/math/function/font_style/monospace.rb +1 -1
  91. data/lib/plurimath/math/function/font_style/normal.rb +22 -1
  92. data/lib/plurimath/math/function/font_style/sans-serif.rb +1 -1
  93. data/lib/plurimath/math/function/font_style/script.rb +1 -1
  94. data/lib/plurimath/math/function/frac.rb +2 -1
  95. data/lib/plurimath/math/function/hat.rb +2 -1
  96. data/lib/plurimath/math/function/int.rb +3 -2
  97. data/lib/plurimath/math/function/lg.rb +1 -1
  98. data/lib/plurimath/math/function/linebreak.rb +2 -1
  99. data/lib/plurimath/math/function/ln.rb +1 -1
  100. data/lib/plurimath/math/function/log.rb +4 -1
  101. data/lib/plurimath/math/function/longdiv.rb +3 -0
  102. data/lib/plurimath/math/function/mpadded.rb +2 -1
  103. data/lib/plurimath/math/function/msgroup.rb +3 -0
  104. data/lib/plurimath/math/function/multiscript.rb +4 -0
  105. data/lib/plurimath/math/function/nary.rb +9 -3
  106. data/lib/plurimath/math/function/norm.rb +3 -1
  107. data/lib/plurimath/math/function/obrace.rb +2 -1
  108. data/lib/plurimath/math/function/oint.rb +3 -2
  109. data/lib/plurimath/math/function/overleftrightarrow.rb +2 -1
  110. data/lib/plurimath/math/function/overset.rb +2 -1
  111. data/lib/plurimath/math/function/power.rb +1 -1
  112. data/lib/plurimath/math/function/power_base.rb +1 -1
  113. data/lib/plurimath/math/function/prod.rb +3 -2
  114. data/lib/plurimath/math/function/sec.rb +1 -1
  115. data/lib/plurimath/math/function/sech.rb +1 -1
  116. data/lib/plurimath/math/function/semantics.rb +35 -0
  117. data/lib/plurimath/math/function/sin.rb +1 -1
  118. data/lib/plurimath/math/function/sinh.rb +1 -1
  119. data/lib/plurimath/math/function/sqrt.rb +3 -2
  120. data/lib/plurimath/math/function/sum.rb +3 -2
  121. data/lib/plurimath/math/function/table/align.rb +1 -1
  122. data/lib/plurimath/math/function/table/bmatrix.rb +1 -1
  123. data/lib/plurimath/math/function/table/cases.rb +4 -0
  124. data/lib/plurimath/math/function/table/multline.rb +1 -1
  125. data/lib/plurimath/math/function/table/pmatrix.rb +1 -1
  126. data/lib/plurimath/math/function/table/split.rb +1 -1
  127. data/lib/plurimath/math/function/table/vmatrix.rb +1 -1
  128. data/lib/plurimath/math/function/table.rb +19 -2
  129. data/lib/plurimath/math/function/tan.rb +1 -1
  130. data/lib/plurimath/math/function/tanh.rb +1 -1
  131. data/lib/plurimath/math/function/td.rb +4 -0
  132. data/lib/plurimath/math/function/ternary_function.rb +2 -2
  133. data/lib/plurimath/math/function/text.rb +3 -0
  134. data/lib/plurimath/math/function/tilde.rb +2 -1
  135. data/lib/plurimath/math/function/tr.rb +3 -0
  136. data/lib/plurimath/math/function/ubrace.rb +2 -1
  137. data/lib/plurimath/math/function/ul.rb +2 -1
  138. data/lib/plurimath/math/function/unary_function.rb +1 -1
  139. data/lib/plurimath/math/function/uncertainty.rb +26 -11
  140. data/lib/plurimath/math/function/underset.rb +2 -1
  141. data/lib/plurimath/math/function/unitsml.rb +18 -6
  142. data/lib/plurimath/math/function/vec.rb +2 -1
  143. data/lib/plurimath/math/number.rb +30 -12
  144. data/lib/plurimath/math/symbol_index.rb +10254 -0
  145. data/lib/plurimath/math/symbol_table.rb +89 -0
  146. data/lib/plurimath/math/symbols/paren.rb +7 -0
  147. data/lib/plurimath/math/symbols/symbol.rb +30 -3
  148. data/lib/plurimath/math/symbols.rb +81 -5
  149. data/lib/plurimath/math.rb +8 -0
  150. data/lib/plurimath/mathml/parser.rb +2 -0
  151. data/lib/plurimath/mathml/translator.rb +17 -3
  152. data/lib/plurimath/native_kernel.rb +64 -0
  153. data/lib/plurimath/omml/parser.rb +1 -0
  154. data/lib/plurimath/omml/translator.rb +2 -1
  155. data/lib/plurimath/pma.rb +19 -0
  156. data/lib/plurimath/unicode_math/constants.rb +0 -258
  157. data/lib/plurimath/unicode_math/mapper/capture_order.rb +142 -0
  158. data/lib/plurimath/unicode_math/mapper/rules.rb +4086 -0
  159. data/lib/plurimath/unicode_math/mapper.rb +146 -0
  160. data/lib/plurimath/unicode_math/parse.rb +54 -294
  161. data/lib/plurimath/unicode_math/parser.rb +71 -2
  162. data/lib/plurimath/unicode_math.rb +1 -2
  163. data/lib/plurimath/unitsml.rb +0 -1
  164. data/lib/plurimath/utility.rb +34 -12
  165. data/lib/plurimath/version.rb +1 -1
  166. data/lib/plurimath.rb +42 -3
  167. data/plurimath.gemspec +3 -1
  168. data/scripts/check_gum_references.rb +69 -0
  169. data/scripts/check_suite_budget.rb +329 -0
  170. data/scripts/docs/asciimath_tables.rb +363 -0
  171. data/scripts/docs/core_inventory.rb +432 -0
  172. data/scripts/docs/format_specs.rb +672 -0
  173. data/scripts/generate_symbol_index.rb +118 -0
  174. data/scripts/measure_require.rb +362 -0
  175. data/scripts/replay_asciimath_corpus.rb +7 -7
  176. data/scripts/replay_stage_roundtrips.rb +242 -0
  177. data/scripts/require_baseline.json +34 -0
  178. data/scripts/roundtrip_cases.rb +462 -0
  179. metadata +97 -16
  180. data/lib/plurimath/asciimath/transform.rb +0 -1395
  181. data/lib/plurimath/latex/transform.rb +0 -1006
  182. data/lib/plurimath/unicode_math/parsing_rules/absence_rules.rb +0 -150
  183. data/lib/plurimath/unicode_math/parsing_rules/common_rules.rb +0 -136
  184. data/lib/plurimath/unicode_math/parsing_rules/constants_rules.rb +0 -136
  185. data/lib/plurimath/unicode_math/parsing_rules/helper.rb +0 -13
  186. data/lib/plurimath/unicode_math/parsing_rules/masked.rb +0 -84
  187. data/lib/plurimath/unicode_math/parsing_rules/sub_sup.rb +0 -281
  188. data/lib/plurimath/unicode_math/parsing_rules.rb +0 -14
  189. data/lib/plurimath/unicode_math/transform.rb +0 -4058
@@ -0,0 +1,578 @@
1
+ = AsciiMath in Plurimath
2
+ :toc:
3
+
4
+ This page specifies the AsciiMath that Plurimath reads and writes: where the
5
+ grammar comes from, the whitespace and number rules, how terms group, the
6
+ symbol and operator vocabulary, the uncertainty call forms, the errors, and
7
+ the corpora that check all of this. It is part of the specification set
8
+ tracked in https://github.com/plurimath/plurimath/issues/525[#525].
9
+
10
+ The grammar and vocabulary excerpts on this page are generated from the amath
11
+ grammar artifact and checked against its recorded checksums (see
12
+ <<regenerating>>). Everything else describes behaviour of the code at the
13
+ revisions named in <<source-of-truth>>.
14
+
15
+ The measurement model behind `ustd`, `uexp` and `ucov`, and how it is carried
16
+ in the other formats, is in link:uncertainty.adoc[Measurement uncertainty].
17
+
18
+ == Status
19
+
20
+ Each form is either *implemented* (merged and released, or merged and pinned
21
+ by Plurimath) or *proposed* (described in an open issue, not built).
22
+
23
+ [cols="3,1,3"]
24
+ |===
25
+ |Form |Status |Where
26
+
27
+ |AsciiMath parsed by the amath gem from its PARG artifact
28
+ |implemented
29
+ |https://github.com/plurimath/plurimath/pull/498[#498],
30
+ https://github.com/plurimath/plurimath/pull/534[#534] (released gem),
31
+ https://github.com/plurimath/plurimath/pull/544[#544] (`+amath ~> 0.1.0+` pin)
32
+
33
+ |`ustd`, `uexp`, `ucov` call forms
34
+ |implemented
35
+ |https://github.com/plurimath/plurimath/pull/499[#499], issue
36
+ https://github.com/plurimath/plurimath/issues/493[#493]
37
+
38
+ |Malformed call read as letters, not as an error
39
+ |implemented
40
+ |https://github.com/plurimath/amath/pull/9[amath#9]
41
+
42
+ |Shared decimal exponent in the call forms, such as `ustd((3.85, 0.23)e4)`
43
+ |implemented
44
+ |https://github.com/plurimath/plurimath/pull/526[#526],
45
+ https://github.com/plurimath/amath/pull/15[amath#15]; issue
46
+ https://github.com/plurimath/amath/issues/13[amath#13] is still open
47
+
48
+ |Rejection of a likely decimal comma inside a call under a non-dot locale
49
+ |implemented
50
+ |https://github.com/plurimath/plurimath/pull/509[#509]
51
+
52
+ |`Amath.parse_model` returning a typed model (amath 0.2.0)
53
+ |proposed
54
+ |amath `docs/stage-contract.adoc`, issue
55
+ https://github.com/plurimath/plurimath/issues/541[#541]
56
+
57
+ |Dialect P ("pma"), a separate strict ASCII grammar with reserved
58
+ constructors, read with the `:pma` input type
59
+ |implemented
60
+ |https://github.com/plurimath/amath/issues/16[amath#16], specified in link:pma.adoc[pma]
61
+
62
+ |Moving the bareword call forms into dialect P
63
+ |proposed
64
+ |https://github.com/plurimath/amath/issues/17[amath#17], waits on amath#16
65
+
66
+ |Inline `(10.3 +/- 0.2)` sugar, relative `%` uncertainty, distribution tags
67
+ |implemented in dialect P only
68
+ |https://github.com/plurimath/plurimath/issues/524[#524], link:pma.adoc[pma]
69
+
70
+ |A closed expression language with evaluation
71
+ |proposed
72
+ |https://github.com/plurimath/plurimath/issues/513[#513],
73
+ https://github.com/plurimath/plurimath/issues/514[#514]
74
+ |===
75
+
76
+ The bareword `ustd`, `uexp` and `ucov` forms are implemented and stay
77
+ supported. amath#17 proposes moving them into dialect P once that dialect
78
+ exists; nothing on this page changes until that happens.
79
+
80
+ [[source-of-truth]]
81
+ == Grammar source of truth
82
+
83
+ Plurimath has no AsciiMath grammar of its own. `Plurimath::Asciimath::Parser`
84
+ calls `Amath.parse_model(text, dialect: :legacy, engine: :ruby, decimal:
85
+ Plurimath.configuration.decimal)`, and `Plurimath::Asciimath::Mapper` turns
86
+ the returned typed model (`Amath::Model::Document`) into a Formula with the
87
+ rule table `Plurimath::Asciimath::Mapper::Rules`. The `:pma` input type
88
+ goes through the same parser and mapper with `dialect: :pma`
89
+ (link:pma.adoc#selection[pma selection]); `:asciimath` is always dialect L.
90
+ `Plurimath::Asciimath::Parse` stays as a tree-level entry point that returns
91
+ amath's internal legacy-shaped raw tree; Plurimath's own parsing does not use
92
+ it. The grammar is
93
+ the amath PARG artifact `artifacts/asciimath.json`, compiled by Parsanol from
94
+ `grammars/asciimath.parg`. That file is itself generated from
95
+ `grammars/asciimath.parg.template` and `data/vocabulary.json`, which amath
96
+ exports once from a pinned Plurimath revision. amath's own contracts are
97
+ `docs/parser-contract.adoc` (the typed-model API that Plurimath uses) and
98
+ `docs/stage-contract.adoc` (the shared profile for the stage gems).
99
+
100
+ Before the grammar runs, `Plurimath::Asciimath::Parser` replaces the bracket
101
+ digraphs `|:` and `:|` by `|`, `{:` by U+2112, `:}` by U+211B, `(:` by U+1455
102
+ and `:)` by U+1450. Those four code points are the invisible-bracket
103
+ characters the grammar matches.
104
+
105
+ Plurimath runs the released amath gem, pinned to `+~> 0.1.0+`. The documented
106
+ amath revision below may be newer than the released gem. The table records
107
+ both, and the generator refuses to run unless the uncertainty rules of the
108
+ two are byte-identical. The grammars differ only in the entry rule: newer
109
+ revisions wrap the whole tree in one `root` capture for the typed-model
110
+ bindings, which amath removes before returning the tree.
111
+
112
+ // BEGIN GENERATED provenance
113
+ [cols="2,3"]
114
+ |===
115
+ |Item |Value
116
+
117
+ |amath revision documented |`667b517bb81276bf920f6f9fea940ae342d8c9e3`
118
+ |`grammars/asciimath.parg` |`sha256:8fc391577745dec67f31913a54de1060c6856f5514ed3c11300724a462ae8cfb`
119
+ |`data/vocabulary.json` |`sha256:768016984baee3ea142b69f0f31f4b9e9255ddedea078d6c7fb1728dc718695c`
120
+ |vocabulary format |`amath-vocabulary/v2`
121
+ |vocabulary registry edition |`1`
122
+ |vocabulary oracle (Plurimath revision) |`ae20be111a26fdd988c58be0bfb2b7f6fe6e352f`
123
+ |artifact checksum (`artifacts/asciimath.json`) |`sha256:38d3d04d938de7a1989dd7779e725c1c19d6f6fb38a3a65feb4bcc5c8395f369`
124
+ |artifact compiler (Parsanol) |`1.3.134`
125
+ |artifact entries |`dot comma arabic`
126
+ |runtime amath gem |`0.2.0`
127
+ |runtime artifact checksum |`sha256:38d3d04d938de7a1989dd7779e725c1c19d6f6fb38a3a65feb4bcc5c8395f369`
128
+ |runtime grammar source hash |`sha256:8fc391577745dec67f31913a54de1060c6856f5514ed3c11300724a462ae8cfb`
129
+ |runtime artifact compiler (Parsanol) |`1.3.134`
130
+ |uncertainty rules, runtime vs documented revision |`byte-identical`
131
+ |===
132
+ // END GENERATED provenance
133
+
134
+ == Whitespace and numbers
135
+
136
+ The lexical rules, quoted from the grammar for the `dot` entry (the `comma`
137
+ and `arabic` entries differ only in the decimal marker):
138
+
139
+ // BEGIN GENERATED lexical-grammar
140
+ [source]
141
+ ----
142
+ space = " " / "\t" / "\n" / "\r" / %x0B / %x0C
143
+ space_opt = [space]
144
+ comma = "," space_opt
145
+ digit = %x30-39
146
+ decimal_marker__dot = "."
147
+ decimal_number__dot = (1*digit decimal_marker__dot 1*digit) / 1*digit
148
+ number__dot = hex_number / binary_number / octal_number / (decimal_number__dot) as number / ((".") as spelling) as symbol
149
+ signed_number__dot = ("-" decimal_operand__dot) as number
150
+ hex_number = ("0x" / "0X") ((1*(%x30-39 / %x41-46 / %x61-66)) as hex_number)
151
+ binary_number = ("0b" / "0B") ((1*(%x30-31)) as binary_number)
152
+ octal_number = ("0o" / "0O") ((1*(%x30-37)) as octal_number)
153
+ base = !"__|" "_"
154
+ power = "^"
155
+ frac_slash = "/" !"/"
156
+ quoted_text = "\"" "unitsml(" ((1*(!(")\"") any_char)) as literal / "" as empty_list) as unitsml ")\"" / "\"" ((1*(!("\"") any_char)) as literal / "" as empty_list) as text "\"" / "\"" ("" as literal) as text
157
+ ----
158
+ // END GENERATED lexical-grammar
159
+
160
+ Whitespace::
161
+ `space` is exactly one character of space, tab, line feed, carriage return,
162
+ vertical tab or form feed. Outside the uncertainty calls, whitespace between
163
+ terms is consumed one character at a time, so runs of any length are allowed
164
+ and do not change the result: `x + y` reads the same as `x + y`. The
165
+ AsciiMath writer emits its own spacing.
166
+
167
+ Whitespace inside uncertainty calls::
168
+ Inside `ustd(`, `uexp(` and `ucov(`, any amount of whitespace may stand after
169
+ the opening bracket, around each comma and before the closing bracket. No
170
+ whitespace may stand between the word and its bracket (`ustd (1.2, 0.1)` is
171
+ the letters `u s t d` followed by a bracketed group), or before the `e` of a
172
+ shared exponent.
173
+
174
+ Numbers::
175
+ A number is digits with an optional single decimal marker followed by more
176
+ digits, or a `0x`, `0b` or `0o` prefixed integer. The decimal marker is the
177
+ one selected by the parse locale (see <<locale>>); there is no exponent
178
+ syntax outside the uncertainty calls, so `1.2e3` is the number `1.2`, the
179
+ letter `e` and the number `3`.
180
+
181
+ [[precedence]]
182
+ == Precedence and grouping
183
+
184
+ AsciiMath has no operator precedence table in the usual sense: `+`, `-`, `=`
185
+ and the other operators are symbols in a flat sequence. Only a few
186
+ constructions take operands, and they bind in this order, tightest first:
187
+
188
+ . Brackets, `+text(...)+`, quoted text and the function words with their
189
+ arguments (`sqrt x`, `frac a b`, `root 3 x`, `+sum_(i=1)^n+`).
190
+ . Subscript `_` and superscript `+^+`. Both may follow the same base, in either
191
+ order (`+a_b^c+`, `+a^b_c+`). A second `+^+` after a superscript is a new
192
+ superscript on the result (`+x^2^3+`).
193
+ . Fraction `/`. A fraction takes the preceding script expression or bracket
194
+ as its numerator and the rest of the following operand as its denominator,
195
+ so it groups to the right: `1/2/3` is `1/(2/3)`, and `+a/b^2+` is
196
+ `+a/(b^2)+`. A function word stays in the numerator: `sqrt x/y` is
197
+ `(sqrt x)/y`. `//` is the slash symbol, not a fraction.
198
+ . `mod`, whose divisor may itself be a fraction (`a mod b/c` is
199
+ `a mod (b/c)`), while a fraction's denominator may hold a `mod`
200
+ (`a/b mod c` is `a/(b mod c)`).
201
+ . Everything else is a left-to-right sequence. `a+b/c+d` is `a`, `+`,
202
+ `b/c`, `+`, `d`. A leading `-` is a symbol, so `-a/b` is `-` followed by
203
+ `a/b`.
204
+
205
+ An uncertainty call is an ordinary operand: `ustd(1.2, 0.1)/2` is a fraction
206
+ whose numerator is the uncertainty, and `+ustd(1.2, 0.1)^2+` puts a superscript
207
+ on it.
208
+
209
+ These groupings follow from the order of the alternatives in the grammar
210
+ rules `iteration`, `frac`, `mod` and `power_base`; the corpus groups `frac`,
211
+ `mod`, `powers` and `operators` (see <<conformance>>) pin them.
212
+
213
+ [[vocabulary]]
214
+ == Symbol and operator table
215
+
216
+ The vocabulary is the data the grammar turns into alternatives. Symbols and
217
+ function words are matched longest first, so `<=>` wins over `<=`, and
218
+ `sinh` over `sin`. The vocabulary lists spellings and their grammar role
219
+ only. The character each symbol produces, and its MathML, OMML, LaTeX and
220
+ UnicodeMath output, comes from Plurimath's symbol classes, which
221
+ link:catalog.adoc[the Ruby reference catalog] exports; the generated
222
+ link:../supported_symbols_list.adoc[symbol list] and
223
+ link:../AsciiMath-Supported-Data.adoc[AsciiMath supported data] pages show
224
+ them per symbol.
225
+
226
+ // BEGIN GENERATED vocabulary-summary
227
+ [cols="1,2,1"]
228
+ |===
229
+ |Category |Spelling shape |Entries
230
+
231
+ |`fonts` |all |14
232
+ |`special_fonts` |all |5
233
+ |`symbol` |all |3267
234
+ |`unary_class` |all |44
235
+ |`symbol` |ASCII letters only |155
236
+ |`symbol` |ASCII punctuation only |68
237
+ |`symbol` |other ASCII spellings |8
238
+ |`symbol` |non-ASCII characters |1
239
+ |`symbol` |quoted names such as `"P{name}"` |1626
240
+ |`symbol` |numeric character references such as `+&#x2213;+` |1409
241
+ |===
242
+ // END GENERATED vocabulary-summary
243
+
244
+ Function words and brackets, by grammar role:
245
+
246
+ // BEGIN GENERATED vocabulary-classes
247
+ ....
248
+ unary_class (44):
249
+ abs arccos arcsin arctan bar cancel
250
+ ceil cos cosh cot coth csc
251
+ csch ddot det dim dot exp
252
+ floor gcd glb hat lcm ln
253
+ lub max min norm obrace overbrace
254
+ sec sech sin sinh sqrt tan
255
+ tanh text tilde ubrace ul underbrace
256
+ underline vec
257
+
258
+ fonts (14):
259
+ bb bbb cc fr ii mathbb mathbf
260
+ mathcal mathfrak mathsf mathtt rm sf tt
261
+
262
+ special_fonts (5):
263
+ CC NN QQ RR ZZ
264
+
265
+ binary_classes (5):
266
+ frac overset root stackrel underset
267
+
268
+ ternary_classes (4):
269
+ int oint prod sum
270
+
271
+ sub_sup_classes (2):
272
+ lim log
273
+
274
+ unary_underscore_words (2):
275
+ ubrace underbrace
276
+
277
+ parenthesis:
278
+ U+1455 U+1450 U+2112 U+211B ( ) { } [ ]
279
+
280
+ table_parenthesis:
281
+ U+1455 U+1450 U+2112 U+211B [ ] ( )
282
+
283
+ decimal_markers:
284
+ U+002E U+002C U+066B
285
+
286
+ base_number_prefix:
287
+ hex_number binary_number octal_number
288
+
289
+ ....
290
+ // END GENERATED vocabulary-classes
291
+
292
+ Symbol spellings other than quoted names and numeric character references,
293
+ which are counted above and not repeated here. `␣` marks a space that is part
294
+ of the spelling.
295
+
296
+ // BEGIN GENERATED vocabulary-symbols
297
+ ....
298
+ word (155):
299
+ AA Delta EE
300
+ Gamma Lambda Leftarrow
301
+ Leftrightarrow Omega Phi
302
+ Pi Psi Rightarrow
303
+ Sigma TT Theta
304
+ Xi aleph alpha
305
+ angle approx ast
306
+ backslash because beta
307
+ bigcap bigcup bigvee
308
+ bigwedge bot bowtie
309
+ cap cdot cdots
310
+ chi circ cong
311
+ cup darr ddots
312
+ del delta diamond
313
+ div downarrow emptyset
314
+ epsilon equiv eta
315
+ exists forall frown
316
+ gamma ge gg
317
+ grad gt hArr
318
+ harr if iff
319
+ implies in infty
320
+ iota kappa lArr
321
+ lambda langle larr
322
+ lceiling ldots le
323
+ leftarrow leftrightarrow lfloor
324
+ ll lt ltimes
325
+ mapsto mgt mlt
326
+ models mu nabla
327
+ ne neg nnn
328
+ not notin nu
329
+ odot omega oo
330
+ oplus otimes ox
331
+ partial phi pi
332
+ pm prec preceq
333
+ prime prop propto
334
+ psi quad rArr
335
+ rangle rarr rceiling
336
+ rfloor rho rightarrow
337
+ rightarrowtail rtimes sigma
338
+ square star sub
339
+ sube subset subseteq
340
+ succ succeq sup
341
+ supe supset supseteq
342
+ tau therefore theta
343
+ times to top
344
+ triangle twoheadrightarrow twoheadrightarrowtail
345
+ uarr uparrow upsilon
346
+ uu uuu varepsilon
347
+ varphi vartheta vdash
348
+ vdots vee vv
349
+ vvv wedge xi
350
+ xx zeta
351
+
352
+ operator (68):
353
+ ! != # $ % & ' '' * ** *** +
354
+ +- , - -: -< -<= -= -> ->> . .. ...
355
+ // /_ /_\ : :' :. :} ; < << <= <=>
356
+ = => > >- >-= >-> >->> ><| >= >> ? @
357
+ \ ^ ^^ ^^^ _ __| _|_ {: | |-- |-> |==
358
+ |>< |><| |__ |~ ~ ~= ~| ~~
359
+
360
+ mixed (8):
361
+ !in &gt; &lt; &times; O/ \␣ o+ o.
362
+
363
+ non-ascii (1):
364
+ ¯
365
+
366
+ ....
367
+ // END GENERATED vocabulary-symbols
368
+
369
+ [[uncertainty]]
370
+ == Uncertainty call forms
371
+
372
+ The three words `ustd`, `uexp` and `ucov` start a call that reads as one
373
+ `Plurimath::Math::Function::Uncertainty` node. The normative rules, quoted
374
+ from the grammar:
375
+
376
+ // BEGIN GENERATED uncertainty-grammar
377
+ [source]
378
+ ----
379
+ unc_number = 1*digit ["." 1*digit]
380
+ unc_value = (["-"] unc_number) as value
381
+ unc_sep = *space "," *space
382
+ unc_unit = unc_sep "\"unitsml(" ((1*(!(")\"") any_char)) as literal / "" as empty_list) as unit ")\""
383
+ unc_open = "(" *space
384
+ unc_close = *space ")"
385
+ unc_shared_exponent = "e" (["+" / "-"] 1*digit) as shared_exponent
386
+ unc_measured_pair = unc_value unc_sep (unc_number) as magnitude
387
+ unc_scaled_pair = unc_open unc_measured_pair unc_close unc_shared_exponent
388
+ unc_measured_interval = unc_value unc_sep (unc_number) as lower unc_sep (unc_number) as upper
389
+ unc_scaled_interval = unc_open unc_measured_interval unc_close unc_shared_exponent
390
+ unc_standard = unc_scaled_pair / unc_value (unc_sep (unc_number) as magnitude / "(" (1*digit) as concise_digits ")" [unc_shared_exponent])
391
+ uncertainty = (uncertainty_std / uncertainty_exp / uncertainty_cov) as uncertainty
392
+ uncertainty_std = ("ustd") as uncertainty_class unc_open unc_standard [unc_unit] unc_close
393
+ uncertainty_exp = ("uexp") as uncertainty_class unc_open (unc_scaled_pair / unc_measured_pair) unc_sep (unc_number) as coverage_factor [unc_sep (unc_number) as coverage_probability] [unc_unit] unc_close
394
+ uncertainty_cov = ("ucov") as uncertainty_class unc_open (unc_scaled_interval / unc_measured_interval) unc_sep (unc_number) as coverage_probability [unc_sep ("shortest" / "symmetric") as interval_kind] [unc_unit] unc_close
395
+ ----
396
+
397
+ Checksum of the excerpt (lines joined by line feeds): `sha256:5b0145491639cb5e5d10e9ac0a9df6d8ae3a2dd3a360d4f4fc8ad2a80a128027`.
398
+ // END GENERATED uncertainty-grammar
399
+
400
+ The forms they accept:
401
+
402
+ [cols="2,3"]
403
+ |===
404
+ |Form |Meaning
405
+
406
+ |`ustd(y, u)`
407
+ |standard uncertainty, absolute notation
408
+
409
+ |`ustd(y(d))`
410
+ |standard uncertainty, concise notation: `ustd(1.230(4))` means `u = 0.004`
411
+
412
+ |`uexp(y, U, k)`, `uexp(y, U, k, p)`
413
+ |expanded uncertainty with coverage factor `k` and optional coverage
414
+ probability `p`
415
+
416
+ |`ucov(y, lo, hi, p)`, `ucov(y, lo, hi, p, shortest)`, `ucov(y, lo, hi, p, symmetric)`
417
+ |coverage interval with its endpoints, coverage probability and optional
418
+ interval choice; `symmetric` is the probabilistically symmetric interval
419
+
420
+ |any of the above with a last argument `+"unitsml(...)"+`
421
+ |the same result with a UnitsML unit, for example
422
+ `uexp(100.02147, 0.00079, 2, "unitsml(g)")`
423
+ |===
424
+
425
+ Only the value `y` may carry a minus sign. Every number uses a full stop as
426
+ its decimal marker and has no exponent of its own. The interval choice is
427
+ written `symmetric` because AsciiMath would read the `-` of
428
+ `probabilistically-symmetric` as minus. The numbers are kept as written:
429
+ `1.230` stays `1.230`, and no floating-point value is computed.
430
+
431
+ === Shared exponents
432
+
433
+ The measured fields may be grouped and followed by `e` and a signed or
434
+ unsigned integer, which applies to the value and the magnitude, or to the
435
+ value and both endpoints. Coverage factors and probabilities are not scaled.
436
+
437
+ [cols="2,2"]
438
+ |===
439
+ |Input |Written back
440
+
441
+ |`ustd((3.85, 0.23)e4)` |`ustd(38500, 2300)`
442
+ |`ustd(680(41)e-3)` |`ustd(0.680(41))`
443
+ |`uexp((3.85, 0.23)e+4, 2, 0.95)` |`uexp(38500, 2300, 2, 0.95)`
444
+ |`ucov((1.024, 0.983, 1.088)e-3, 0.95, shortest)` |`ucov(0.001024, 0.000983, 0.001088, 0.95, shortest)`
445
+ |===
446
+
447
+ The shift is done on the decimal text. For concise notation the magnitude is
448
+ derived from the value's decimal places before the shift. A shift that would
449
+ make a number longer than 1,000,000 characters is rejected. The writer emits
450
+ the shifted numbers, not the exponent.
451
+
452
+ === When a call is not an uncertainty
453
+
454
+ A call that does not match these rules is not an error. The grammar falls back
455
+ to the reading it had before the words existed, letter by letter
456
+ (amath#9):
457
+
458
+ * `uexp(10.0, 0.3)` (no `k`) is `u` followed by `exp(10.0, 0.3)`;
459
+ * `ustd(1.2, -0.1)`, `ustd(1.2e3, 0.1)`, `ustd (1.2, 0.1)` and
460
+ `ustd((3.85, 0.23) e4)` are the letters `u s t d` followed by a bracketed
461
+ group;
462
+ * `+-` and `pm` are always the plus-minus symbol (`Math::Symbols::Pm`), a
463
+ literal `±` is an ordinary symbol, and `1.230(4)` outside a call is a number
464
+ followed by a bracketed group. None of them starts an uncertainty.
465
+
466
+ A call that matches the rules but whose numbers are not a valid result, such
467
+ as `uexp(1.0, 0.3, 0, 0.95)` (`k` must be greater than 0) or
468
+ `ucov(1.0, 2.0, 1.5, 0.95)` (the lower endpoint is above the upper one), is an
469
+ error (see <<errors>>).
470
+
471
+ The parse tree each form produces, before Plurimath turns it into a node, is
472
+ recorded in link:uncertainty-asciimath-tree.md[the AsciiMath uncertainty tree
473
+ contract].
474
+
475
+ [[locale]]
476
+ === Locale and decimal commas
477
+
478
+ `+Plurimath::Math.parse(text, :asciimath, locale: ...)+` selects the decimal
479
+ marker for ordinary numbers: amath has one artifact entry per marker (`dot`,
480
+ `comma` and `arabic`). Inside an uncertainty call the marker is always a full
481
+ stop and the argument separator is always a comma.
482
+
483
+ Under a locale whose decimal marker is not a full stop, a call in which a
484
+ separator comma is directly followed by a digit, such as `ustd(1,5)` or
485
+ `ustd(1.2,0.1)` under `locale: :fr`, raises
486
+ `Plurimath::Math::AmbiguousDecimalError` instead of guessing.
487
+ `ustd(1.230, 0.004)`, with a space after each comma, reads the same under every
488
+ locale. Calls inside quoted text are not checked. A value written with the
489
+ Arabic decimal mark, such as `ustd(1٫230, 0٫004)` under `locale: :ar`, is not
490
+ an uncertainty call and reads as letters followed by a bracketed group.
491
+
492
+ [[errors]]
493
+ == Errors
494
+
495
+ `Plurimath::Math.parse(text, :asciimath)` raises these errors. All of them are
496
+ `Plurimath::Math::ParseError`, which is a `Plurimath::Error`.
497
+
498
+ [cols="2,3"]
499
+ |===
500
+ |Error |Raised when
501
+
502
+ |`Plurimath::Math::ParseError`
503
+ |amath rejects the input (`Amath::ParseError`, including
504
+ `Amath::NestingTooDeep` for input nested too deeply for the engine), or the
505
+ transform cannot build a node from the tree. An uncertainty call whose
506
+ numbers fail validation is reported this way: the
507
+ `Plurimath::Errors::InvalidUncertainty` raised inside is not kept as the
508
+ `cause`.
509
+
510
+ |`Plurimath::Math::AmbiguousDecimalError` (a `ParseError`)
511
+ |a call has a likely decimal comma under a non-dot locale (<<locale>>). It is
512
+ raised before amath runs and reaches the caller unchanged.
513
+
514
+ |`Plurimath::Math::MissingDependencyError` (a `ParseError`)
515
+ |the amath gem cannot be loaded.
516
+ |===
517
+
518
+ Building the node directly, with `Plurimath::Math::Function::Uncertainty.new`,
519
+ raises `Plurimath::Errors::InvalidUncertainty` (a `Plurimath::Error`, not a
520
+ `ParseError`) for any field that does not form a valid result. Unknown parse
521
+ options raise `Plurimath::Errors::ParseOptionError`, and an unknown format
522
+ name raises `Plurimath::Math::InvalidTypeError`.
523
+
524
+ amath's own error classes (`Amath::ParseError` with `byte_offset` and
525
+ `char_offset`, `Amath::NestingTooDeep`, `Amath::ArtifactError`,
526
+ `Amath::EngineUnavailable`) are specified in amath's
527
+ `docs/parser-contract.adoc`. Plurimath turns an amath rejection into a
528
+ `Parslet::ParseFailed` positioned at amath's byte offset before
529
+ `Plurimath::Math.parse` reports it.
530
+
531
+ [[conformance]]
532
+ == Conformance
533
+
534
+ These corpora and fixtures check the behaviour on this page:
535
+
536
+ * plurimath-testsuite, `stages/asciimath/generations/000001/payloads/`, layout
537
+ revision 1 (plurimath-testsuite `6f8bc12`). Its 28 groups, with 173 rows,
538
+ cover numbers, whitespace, symbols, operators, fractions, `mod`, powers,
539
+ roots, fonts, matrices, brackets, quoted text, UnitsML, number formatting
540
+ and rejections. Generation 1 is a byte copy of `corpus/asciimath/` at
541
+ plurimath-testsuite `281059b`, which is the commit of Plurimath's
542
+ `submodules/plurimath-testsuite`; Plurimath replays it with
543
+ `scripts/replay_asciimath_corpus.rb` and
544
+ `spec/plurimath/asciimath_corpus_replay_spec.rb`.
545
+ * amath `corpora/uncertainty-shared-exponent.yml`: the input, tree and exact
546
+ decimal expectation for every shared-exponent form, checked by amath's
547
+ specs and by `spec/plurimath/asciimath/uncertainty_shared_exponent_spec.rb`.
548
+ * Plurimath `spec/plurimath/fixtures/uncertainty/contract.yml`: the exact
549
+ output of every uncertainty form in every format, its invalid cases, and the
550
+ ordinary `±` cases, checked by `spec/plurimath/uncertainty_contract_spec.rb`
551
+ and `spec/plurimath/asciimath/uncertainty_spec.rb`.
552
+ * `spec/plurimath/asciimath/uncertainty_locale_spec.rb` and
553
+ `spec/plurimath/asciimath/uncertainty_contexts_spec.rb`: the locale rule and
554
+ uncertainty calls inside other constructions.
555
+
556
+ The testsuite has no uncertainty rows yet. They are expected as a later
557
+ asciimath generation, appended under the stage layout of
558
+ https://github.com/plurimath/plurimath/issues/540[#540].
559
+
560
+ [[regenerating]]
561
+ == Regenerating this page
562
+
563
+ The regions between `// BEGIN GENERATED` and `// END GENERATED` comments are
564
+ written by `scripts/docs/asciimath_tables.rb`. Do not edit them by hand. To
565
+ update them after an amath change, check out amath at the revision to
566
+ document and run, from the Plurimath checkout:
567
+
568
+ [source,sh]
569
+ ----
570
+ bundle exec ruby scripts/docs/asciimath_tables.rb --amath ../amath
571
+ ----
572
+
573
+ The script checks `grammars/asciimath.parg` and `data/vocabulary.json`
574
+ against `artifacts/asciimath.checksums.txt` in that checkout, and compares the
575
+ uncertainty rules with the artifact of the amath gem the bundle loads. It
576
+ stops without writing anything when either check fails. Add `--check` to
577
+ verify that this page is current without changing it; it then exits 1 when the
578
+ page is out of date.