unitsml 0.4.7 → 0.6.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/rake.yml +8 -0
- data/.github/workflows/release.yml +7 -1
- data/.rubocop.yml +18 -0
- data/.rubocop_todo.yml +498 -0
- data/Gemfile +16 -11
- data/Rakefile +5 -3
- data/bin/console +4 -3
- data/docs/README.adoc +106 -4
- data/lib/unitsml/dimension.rb +49 -35
- data/lib/unitsml/errors/base_error.rb +8 -0
- data/lib/unitsml/errors/plurimath_load_error.rb +1 -1
- data/lib/unitsml/errors.rb +8 -0
- data/lib/unitsml/extender.rb +21 -16
- data/lib/unitsml/fenced.rb +97 -0
- data/lib/unitsml/fenced_numeric.rb +13 -0
- data/lib/unitsml/formula.rb +46 -33
- data/lib/unitsml/intermediate_exp_rules.rb +76 -0
- data/lib/unitsml/model/dimension.rb +4 -14
- data/lib/unitsml/model/dimension_quantities/quantity.rb +2 -0
- data/lib/unitsml/model/dimension_quantities.rb +17 -0
- data/lib/unitsml/model/prefix.rb +4 -8
- data/lib/unitsml/model/prefixes/name.rb +5 -4
- data/lib/unitsml/model/prefixes/symbol.rb +3 -2
- data/lib/unitsml/model/prefixes.rb +10 -0
- data/lib/unitsml/model/quantities/name.rb +4 -3
- data/lib/unitsml/model/quantities.rb +9 -0
- data/lib/unitsml/model/quantity.rb +5 -7
- data/lib/unitsml/model/unit.rb +4 -9
- data/lib/unitsml/model/units/enumerated_root_unit.rb +1 -0
- data/lib/unitsml/model/units/name.rb +4 -3
- data/lib/unitsml/model/units/root_units.rb +3 -2
- data/lib/unitsml/model/units/symbol.rb +2 -1
- data/lib/unitsml/model/units/system.rb +4 -3
- data/lib/unitsml/model/units.rb +13 -0
- data/lib/unitsml/model.rb +15 -0
- data/lib/unitsml/namespace.rb +8 -0
- data/lib/unitsml/number.rb +79 -0
- data/lib/unitsml/parse.rb +30 -38
- data/lib/unitsml/parser.rb +27 -13
- data/lib/unitsml/prefix.rb +17 -17
- data/lib/unitsml/sqrt.rb +3 -3
- data/lib/unitsml/transform.rb +143 -50
- data/lib/unitsml/unit.rb +67 -37
- data/lib/unitsml/unitsdb/dimension.rb +14 -20
- data/lib/unitsml/unitsdb/dimension_quantity.rb +2 -6
- data/lib/unitsml/unitsdb/dimensions.rb +3 -9
- data/lib/unitsml/unitsdb/prefix_reference.rb +23 -0
- data/lib/unitsml/unitsdb/prefixes.rb +17 -5
- data/lib/unitsml/unitsdb/quantities.rb +4 -4
- data/lib/unitsml/unitsdb/unit.rb +21 -0
- data/lib/unitsml/unitsdb/units.rb +19 -18
- data/lib/unitsml/unitsdb.rb +14 -5
- data/lib/unitsml/utility.rb +133 -103
- data/lib/unitsml/version.rb +3 -1
- data/lib/unitsml.rb +71 -35
- data/unitsdb/Gemfile +6 -0
- data/unitsdb/LICENSE.md +53 -0
- data/unitsdb/README.adoc +1253 -0
- data/unitsdb/RELEASE-NOTES.adoc +269 -0
- data/unitsdb/dimensions.yaml +1607 -602
- data/unitsdb/prefixes.yaml +842 -301
- data/unitsdb/quantities.yaml +3706 -2458
- data/unitsdb/scales.yaml +97 -0
- data/unitsdb/schemas/README.md +159 -0
- data/unitsdb/schemas/dimensions-schema.yaml +153 -0
- data/unitsdb/schemas/prefixes-schema.yaml +155 -0
- data/unitsdb/schemas/quantities-schema.yaml +117 -0
- data/unitsdb/schemas/scales-schema.yaml +106 -0
- data/unitsdb/schemas/unit_systems-schema.yaml +116 -0
- data/unitsdb/schemas/units-schema.yaml +215 -0
- data/unitsdb/spec/units_spec.rb +13 -10
- data/unitsdb/unit_systems.yaml +77 -15
- data/unitsdb/units.yaml +13517 -9974
- data/unitsdb/validate_schemas.rb +203 -0
- data/unitsml.gemspec +4 -1
- metadata +47 -7
- data/lib/unitsml/error.rb +0 -8
- data/unitsdb/docs/README.adoc +0 -12
- data/unitsdb/docs/navigation.adoc +0 -7
data/docs/README.adoc
CHANGED
|
@@ -69,7 +69,7 @@ unit.to_s # => "m"
|
|
|
69
69
|
unit.to_xml
|
|
70
70
|
# =>
|
|
71
71
|
<Unit xmlns="https://schema.unitsml.org/unitsml/1.0" xml:id="U_NISTu1" dimensionURL="#NISTd1">
|
|
72
|
-
<UnitSystem name="SI" type="SI_derived" xml:lang="en
|
|
72
|
+
<UnitSystem name="SI" type="SI_derived" xml:lang="en"/>
|
|
73
73
|
<UnitName xml:lang="en">meter</UnitName>
|
|
74
74
|
<UnitSymbol type="HTML">m</UnitSymbol>
|
|
75
75
|
<UnitSymbol type="MathMl">
|
|
@@ -102,7 +102,7 @@ unit.to_s # => "um"
|
|
|
102
102
|
unit.to_xml
|
|
103
103
|
# =>
|
|
104
104
|
<Unit xmlns="https://schema.unitsml.org/unitsml/1.0" xml:id="U_um" dimensionURL="#NISTd1">
|
|
105
|
-
<UnitSystem name="SI" type="SI_derived" xml:lang="en
|
|
105
|
+
<UnitSystem name="SI" type="SI_derived" xml:lang="en"/>
|
|
106
106
|
<UnitName xml:lang="en">um</UnitName>
|
|
107
107
|
<UnitSymbol type="HTML">µm</UnitSymbol>
|
|
108
108
|
<UnitSymbol type="MathMl"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
|
|
@@ -169,6 +169,104 @@ unit.to_unicode # Unicode representation
|
|
|
169
169
|
unit.to_xml # XML (in UnitsML) representation
|
|
170
170
|
----
|
|
171
171
|
|
|
172
|
+
==== Explicit parenthesis
|
|
173
|
+
|
|
174
|
+
When converting units to formats like MathML or LaTeX, you can specify whether
|
|
175
|
+
to use explicit parentheses for clarity in complex expressions.
|
|
176
|
+
|
|
177
|
+
A pair of parentheses is typically used for two purposes:
|
|
178
|
+
|
|
179
|
+
. For grouping to indicate operator precedence in mathematical expressions. When
|
|
180
|
+
expressing units, most of the time, parentheses are used to indicate operator
|
|
181
|
+
precedence, parentheses are only kept in cases where it indicates precedence.
|
|
182
|
+
When parentheses are not needed for precedence, such as in the expression
|
|
183
|
+
`(m/s)`, the parentheses can be removed whilch simplifies the expression into
|
|
184
|
+
`m/s`.
|
|
185
|
+
|
|
186
|
+
. For indicating that an expression is a compound unit. These parentheses are
|
|
187
|
+
explicit parentheses that cannot be removed without changing the meaning of the
|
|
188
|
+
expression.
|
|
189
|
+
|
|
190
|
+
UnitsML Ruby provides an option `explicit_parenthesis` to control the inclusion
|
|
191
|
+
of parentheses in the output.
|
|
192
|
+
|
|
193
|
+
In an expression:
|
|
194
|
+
|
|
195
|
+
* `(...)` is considered an implicit parenthesis for grouping and operator
|
|
196
|
+
precedence and can be removed without changing the meaning of the expression.
|
|
197
|
+
|
|
198
|
+
* `((...))` is considered an explicit parenthesis, which indicates that the
|
|
199
|
+
parentheses need to be preserved in the output regardless of operator
|
|
200
|
+
precedence.
|
|
201
|
+
|
|
202
|
+
The `explicit_parenthesis` option allows you to control how parentheses are
|
|
203
|
+
considered in the output formats. It can be set to `true` or `false`.
|
|
204
|
+
|
|
205
|
+
Syntax for using `explicit_parenthesis` is as follows:
|
|
206
|
+
|
|
207
|
+
[source,ruby]
|
|
208
|
+
----
|
|
209
|
+
unit.to_asciimath(explicit_parenthesis: true) # or false
|
|
210
|
+
unit.to_mathml(explicit_parenthesis: true) # or false
|
|
211
|
+
unit.to_latex(explicit_parenthesis: true) # or false
|
|
212
|
+
----
|
|
213
|
+
|
|
214
|
+
When `explicit_parenthesis` is set to:
|
|
215
|
+
|
|
216
|
+
`true`:: (default) the usage of `(...)` and `((...))` is differentiated as
|
|
217
|
+
implicit and explicit parentheses, respectively.
|
|
218
|
+
|
|
219
|
+
`false`:: all parentheses are treated as grouping parentheses, there is no
|
|
220
|
+
difference between `(...)` and `((...))`.
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
.By default `explicit_parenthesis` is `true` and double parentheses are considered explicit parentheses
|
|
224
|
+
[example]
|
|
225
|
+
====
|
|
226
|
+
[source, ruby]
|
|
227
|
+
----
|
|
228
|
+
Unitsml.parse("((m/s))").to_asciimath
|
|
229
|
+
> "(m/s^-1)" # Only one of the two pairs of parentheses are included in the output
|
|
230
|
+
----
|
|
231
|
+
====
|
|
232
|
+
|
|
233
|
+
If an extra pair of parentheses is included, it will be considered as grouping
|
|
234
|
+
parentheses and will not be included in the Unitsml::Formula or the output.
|
|
235
|
+
|
|
236
|
+
.By default `explicit_parenthesis` is `true` and double parentheses are considered explicit parentheses
|
|
237
|
+
[example]
|
|
238
|
+
====
|
|
239
|
+
[source, ruby]
|
|
240
|
+
----
|
|
241
|
+
Unitsml.parse("(((m/s)))").to_asciimath
|
|
242
|
+
> "(m/s^-1)" # the third pair of parentheses are ignored as grouping parentheses
|
|
243
|
+
Unitsml.parse("(((((m/s)))))").to_asciimath
|
|
244
|
+
> "((m/s^-1))" # the fifth pair of parentheses are ignored as grouping parentheses
|
|
245
|
+
----
|
|
246
|
+
|
|
247
|
+
[source,ruby]
|
|
248
|
+
----
|
|
249
|
+
unit = Unitsml.parse("((m/s))")
|
|
250
|
+
unit.to_asciimath # equivalent to (explicit_parenthesis: true) => "(m/s^-1)"
|
|
251
|
+
unit = Unitsml.parse("((m/s))*((m/s))")
|
|
252
|
+
unit.to_asciimath(explicit_parenthesis: true) # "(m/s^-1)*(m/s^-1)"
|
|
253
|
+
----
|
|
254
|
+
====
|
|
255
|
+
|
|
256
|
+
When `explicit_parenthesis` is `false`, all parentheses are treated as grouping parentheses
|
|
257
|
+
and therefore will be reduced to only the necessary parentheses for operator precedence.
|
|
258
|
+
|
|
259
|
+
.When `explicit_parenthesis` is `false`, all parentheses are treated as grouping parentheses
|
|
260
|
+
[example]
|
|
261
|
+
====
|
|
262
|
+
[source,ruby]
|
|
263
|
+
----
|
|
264
|
+
unit = Unitsml.parse("(m/s)")
|
|
265
|
+
unit.to_asciimath(explicit_parenthesis: false) # => "m/s"
|
|
266
|
+
unit = Unitsml.parse("((m/s))*((m/s))")
|
|
267
|
+
unit.to_asciimath(explicit_parenthesis: false) # "m/s^-1*m/s^-1"
|
|
268
|
+
----
|
|
269
|
+
====
|
|
172
270
|
|
|
173
271
|
|
|
174
272
|
== Installation
|
|
@@ -196,7 +294,9 @@ $ gem install unitsml
|
|
|
196
294
|
|
|
197
295
|
== Usage
|
|
198
296
|
|
|
199
|
-
*UnitsML* Ruby provides functionality to represent and convert units between
|
|
297
|
+
*UnitsML* Ruby provides functionality to represent and convert units between
|
|
298
|
+
different formats including *MathML*, *LaTeX*, *AsciiMath*, *HTML*, *Unicode*,
|
|
299
|
+
and *XML*(*Unitsml* schema based elements).
|
|
200
300
|
|
|
201
301
|
=== Basic Usage
|
|
202
302
|
|
|
@@ -275,7 +375,9 @@ repository.
|
|
|
275
375
|
|
|
276
376
|
== Development
|
|
277
377
|
|
|
278
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
378
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
379
|
+
`bundle exec spec` to run the tests. You can also run `bin/console` for an
|
|
380
|
+
interactive prompt that will allow you to experiment.
|
|
279
381
|
|
|
280
382
|
=== Submodules
|
|
281
383
|
|
data/lib/unitsml/dimension.rb
CHANGED
|
@@ -9,63 +9,59 @@ module Unitsml
|
|
|
9
9
|
@power_numerator = power_numerator
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def ==(
|
|
13
|
-
self.class ==
|
|
14
|
-
dimension_name ==
|
|
15
|
-
power_numerator ==
|
|
12
|
+
def ==(other)
|
|
13
|
+
self.class == other.class &&
|
|
14
|
+
dimension_name == other&.dimension_name &&
|
|
15
|
+
power_numerator == other&.power_numerator
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def dim_instance
|
|
19
|
-
@
|
|
19
|
+
@dim_instance ||= Unitsdb.dimensions.find_parsables_by_id(dimension_name)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def dim_symbols
|
|
23
|
-
dim_instance.send(
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def to_mathml(
|
|
27
|
-
# MathML key's value in unitsdb/dimensions.yaml
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if power_numerator
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
),
|
|
37
|
-
]
|
|
38
|
-
)
|
|
39
|
-
end
|
|
23
|
+
dim_instance.send(dim_instance.processed_keys.last).symbols.first
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_mathml(options)
|
|
27
|
+
# MathML key's value in unitsdb/dimensions.yaml
|
|
28
|
+
# file includes mi tags only.
|
|
29
|
+
value = ::Mml::V4::Mi.from_xml(dim_symbols.mathml)
|
|
30
|
+
method_name = if power_numerator
|
|
31
|
+
value = msup_tag(value, options)
|
|
32
|
+
:msup
|
|
33
|
+
else
|
|
34
|
+
:mi
|
|
35
|
+
end
|
|
40
36
|
{ method_name: method_name, value: value }
|
|
41
37
|
end
|
|
42
38
|
|
|
43
|
-
def to_latex(
|
|
44
|
-
power_numerator_generic_code(:latex)
|
|
39
|
+
def to_latex(options)
|
|
40
|
+
power_numerator_generic_code(:latex, options)
|
|
45
41
|
end
|
|
46
42
|
|
|
47
|
-
def to_asciimath(
|
|
48
|
-
power_numerator_generic_code(:ascii)
|
|
43
|
+
def to_asciimath(options)
|
|
44
|
+
power_numerator_generic_code(:ascii, options)
|
|
49
45
|
end
|
|
50
46
|
|
|
51
|
-
def to_unicode(
|
|
52
|
-
power_numerator_generic_code(:unicode)
|
|
47
|
+
def to_unicode(options)
|
|
48
|
+
power_numerator_generic_code(:unicode, options)
|
|
53
49
|
end
|
|
54
50
|
|
|
55
|
-
def to_html(
|
|
51
|
+
def to_html(options)
|
|
56
52
|
value = dim_symbols.html
|
|
57
|
-
value = "#{value}
|
|
53
|
+
value = "#{value}#{html_numerator_conversion(options)}" if power_numerator
|
|
58
54
|
value
|
|
59
55
|
end
|
|
60
56
|
|
|
61
57
|
def generate_id
|
|
62
|
-
"#{dimension_name.split('_').last}#{power_numerator}"
|
|
58
|
+
"#{dimension_name.split('_').last}#{power_numerator&.raw_value}"
|
|
63
59
|
end
|
|
64
60
|
|
|
65
61
|
def to_xml(_)
|
|
66
62
|
attributes = {
|
|
67
63
|
symbol: dim_instance.processed_symbol,
|
|
68
|
-
power_numerator: power_numerator || 1
|
|
64
|
+
power_numerator: power_numerator&.raw_value || 1
|
|
69
65
|
}
|
|
70
66
|
Model::DimensionQuantities.const_get(modelize(element_name)).new(attributes)
|
|
71
67
|
end
|
|
@@ -75,20 +71,38 @@ module Unitsml
|
|
|
75
71
|
end
|
|
76
72
|
|
|
77
73
|
def modelize(value)
|
|
78
|
-
value&.split(
|
|
74
|
+
value&.split('_')&.map(&:capitalize)&.join
|
|
79
75
|
end
|
|
80
76
|
|
|
81
77
|
private
|
|
82
78
|
|
|
83
|
-
def
|
|
79
|
+
def html_numerator_conversion(options)
|
|
80
|
+
"<sup>#{power_numerator.to_html(options)}</sup>"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def power_numerator_generic_code(method_name, options)
|
|
84
84
|
value = dim_symbols.public_send(method_name)
|
|
85
85
|
return value unless power_numerator
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
method_name = :asciimath if method_name == :ascii
|
|
88
|
+
"#{value}^#{power_numerator&.public_send(:"to_#{method_name}", options)}"
|
|
88
89
|
end
|
|
89
90
|
|
|
90
91
|
def element_name
|
|
91
92
|
dim_instance.processed_keys.first
|
|
92
93
|
end
|
|
94
|
+
|
|
95
|
+
def msup_tag(value, options)
|
|
96
|
+
mathml = power_numerator.to_mathml(options)
|
|
97
|
+
msup = ::Mml::V4::Msup.new(
|
|
98
|
+
mrow_value: [::Mml::V4::Mrow.new(mi_value: [value])]
|
|
99
|
+
)
|
|
100
|
+
[mathml].flatten.each do |record|
|
|
101
|
+
record_values = msup.public_send("#{record[:method_name]}_value") || []
|
|
102
|
+
record_values += [record[:value]]
|
|
103
|
+
msup.public_send("#{record[:method_name]}_value=", record_values)
|
|
104
|
+
end
|
|
105
|
+
msup
|
|
106
|
+
end
|
|
93
107
|
end
|
|
94
108
|
end
|
data/lib/unitsml/extender.rb
CHANGED
|
@@ -8,22 +8,22 @@ module Unitsml
|
|
|
8
8
|
@symbol = symbol
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def ==(
|
|
12
|
-
self.class ==
|
|
13
|
-
symbol ==
|
|
11
|
+
def ==(other)
|
|
12
|
+
self.class == other.class &&
|
|
13
|
+
symbol == other&.symbol
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def to_mathml(options)
|
|
17
|
-
rspace =
|
|
18
|
-
extender = multiplier(options[:multiplier] ||
|
|
17
|
+
rspace = 'thickmathspace' if options[:multiplier] == :space
|
|
18
|
+
extender = multiplier(options[:multiplier] || '⋅', unicode: true)
|
|
19
19
|
{
|
|
20
20
|
method_name: :mo,
|
|
21
|
-
value: ::Mml::Mo.new(value: extender, rspace: rspace)
|
|
21
|
+
value: ::Mml::V4::Mo.new(value: extender, rspace: rspace)
|
|
22
22
|
}
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def to_latex(options)
|
|
26
|
-
multiplier(options[:multiplier] ||
|
|
26
|
+
multiplier(options[:multiplier] || '/')
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def to_asciimath(options)
|
|
@@ -31,12 +31,11 @@ module Unitsml
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def to_html(options)
|
|
34
|
-
multiplier(options[:multiplier] ||
|
|
34
|
+
multiplier(options[:multiplier] || '⋅', unicode: true, html: true)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def to_unicode(options)
|
|
38
|
-
extender = options[:multiplier] ||
|
|
39
|
-
symbol == "*" ? "·" : symbol
|
|
38
|
+
extender = options[:multiplier] || unicode_extender
|
|
40
39
|
multiplier(extender)
|
|
41
40
|
end
|
|
42
41
|
|
|
@@ -45,16 +44,22 @@ module Unitsml
|
|
|
45
44
|
def multiplier(extender, unicode: false, html: false)
|
|
46
45
|
case extender
|
|
47
46
|
when :space
|
|
48
|
-
|
|
49
|
-
" "
|
|
50
|
-
else
|
|
51
|
-
unicode ? "⁢" : " "
|
|
52
|
-
end
|
|
47
|
+
space_extender(html, unicode)
|
|
53
48
|
when :nospace
|
|
54
|
-
unicode ?
|
|
49
|
+
unicode ? '⁢' : ''
|
|
55
50
|
else
|
|
56
51
|
unicode ? Utility.string_to_html_entity(extender) : extender
|
|
57
52
|
end
|
|
58
53
|
end
|
|
54
|
+
|
|
55
|
+
def space_extender(html, unicode)
|
|
56
|
+
return ' ' if html
|
|
57
|
+
|
|
58
|
+
unicode ? '⁢' : ' '
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def unicode_extender
|
|
62
|
+
symbol == '*' ? '·' : symbol
|
|
63
|
+
end
|
|
59
64
|
end
|
|
60
65
|
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Unitsml
|
|
4
|
+
class Fenced
|
|
5
|
+
include FencedNumeric
|
|
6
|
+
|
|
7
|
+
attr_reader :open_paren, :value, :close_paren
|
|
8
|
+
|
|
9
|
+
def initialize(open_paren, value, close_paren)
|
|
10
|
+
@open_paren = open_paren
|
|
11
|
+
@value = value
|
|
12
|
+
@close_paren = close_paren
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def ==(other)
|
|
16
|
+
self.class == other.class &&
|
|
17
|
+
open_paren == other&.open_paren &&
|
|
18
|
+
value == other&.value &&
|
|
19
|
+
close_paren == other&.close_paren
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_asciimath(options = {})
|
|
23
|
+
fenced_conversion_for(lang: :asciimath, options: options)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_latex(options = {})
|
|
27
|
+
fenced_conversion_for(lang: :latex, options: options)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_mathml(options = {})
|
|
31
|
+
mathml = value.to_mathml(options)
|
|
32
|
+
return mathml unless options[:explicit_parenthesis]
|
|
33
|
+
|
|
34
|
+
fenced = ::Mml::V4::Mrow.new(mo_value: [::Mml::V4::Mo.new(value: open_paren)])
|
|
35
|
+
fenced.ordered = true
|
|
36
|
+
fenced.element_order ||= [xml_order_element('mo')]
|
|
37
|
+
[mathml].flatten.each { |record| add_math_element(fenced, record) }
|
|
38
|
+
fenced.mo_value << ::Mml::V4::Mo.new(value: close_paren)
|
|
39
|
+
fenced.element_order << xml_order_element('mo')
|
|
40
|
+
{ method_name: :mrow, value: fenced }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def negative?
|
|
44
|
+
value.negative?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def update_negative_sign
|
|
48
|
+
value.update_negative_sign
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def float_to_display
|
|
52
|
+
value.float_to_display
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def raw_value
|
|
56
|
+
value.raw_value
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def to_html(options = {})
|
|
60
|
+
fenced_conversion_for(lang: :html, options: options)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def to_unicode(options = {})
|
|
64
|
+
fenced_conversion_for(lang: :unicode, options: options)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def dimensions_extraction
|
|
68
|
+
case value
|
|
69
|
+
when Dimension
|
|
70
|
+
value
|
|
71
|
+
when Formula, Fenced
|
|
72
|
+
value.dimensions_extraction
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def add_math_element(instance, child_hash)
|
|
79
|
+
method_name = child_hash[:method_name]
|
|
80
|
+
method_value = instance.public_send(:"#{method_name}_value") || []
|
|
81
|
+
method_value += Array(child_hash[:value])
|
|
82
|
+
instance.public_send(:"#{method_name}_value=", method_value)
|
|
83
|
+
instance.element_order << xml_order_element(method_name.to_s)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def xml_order_element(tag_name)
|
|
87
|
+
Lutaml::Xml::Element.new('Element', tag_name)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def fenced_conversion_for(lang:, options:)
|
|
91
|
+
lang_value = value.send(:"to_#{lang}", options)
|
|
92
|
+
return lang_value unless options[:explicit_parenthesis]
|
|
93
|
+
|
|
94
|
+
"#{open_paren}#{lang_value}#{close_paren}"
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
data/lib/unitsml/formula.rb
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require "unitsml/utility"
|
|
3
|
+
require 'mml'
|
|
4
|
+
require 'htmlentities'
|
|
6
5
|
|
|
7
6
|
module Unitsml
|
|
8
7
|
class Formula
|
|
@@ -20,25 +19,25 @@ module Unitsml
|
|
|
20
19
|
@norm_text = norm_text
|
|
21
20
|
end
|
|
22
21
|
|
|
23
|
-
def ==(
|
|
24
|
-
self.class ==
|
|
25
|
-
value ==
|
|
26
|
-
explicit_value ==
|
|
27
|
-
root ==
|
|
22
|
+
def ==(other)
|
|
23
|
+
self.class == other.class &&
|
|
24
|
+
value == other&.value &&
|
|
25
|
+
explicit_value == other&.explicit_value &&
|
|
26
|
+
root == other.root
|
|
28
27
|
end
|
|
29
28
|
|
|
30
29
|
def to_mathml(options = {})
|
|
31
30
|
if root
|
|
32
|
-
update_options(options)
|
|
31
|
+
options = update_options(options)
|
|
33
32
|
nullify_mml_models if plurimath_available?
|
|
34
|
-
math = ::Mml::
|
|
33
|
+
math = ::Mml::V4::Math.new(display: 'block')
|
|
35
34
|
math.ordered = true
|
|
36
35
|
math.element_order ||= []
|
|
37
36
|
value.each { |instance| process_value(math, instance.to_mathml(options)) }
|
|
38
|
-
|
|
39
|
-
generated_math = math.to_xml.gsub(/&(.*?)(?=<\/)/, '&\1')
|
|
37
|
+
generated_math = math.to_xml.gsub(%r{&(.*?)(?=</)}, '&\1')
|
|
40
38
|
reset_mml_models if plurimath_available?
|
|
41
|
-
|
|
39
|
+
|
|
40
|
+
generated_math.force_encoding('UTF-8')
|
|
42
41
|
else
|
|
43
42
|
value.map { |obj| obj.to_mathml(options) }
|
|
44
43
|
end
|
|
@@ -61,7 +60,7 @@ module Unitsml
|
|
|
61
60
|
end
|
|
62
61
|
|
|
63
62
|
def to_xml(options = {})
|
|
64
|
-
update_options(options)
|
|
63
|
+
options = update_options(options)
|
|
65
64
|
if (dimensions_array = extract_dimensions(value)).any?
|
|
66
65
|
dimensions(sort_dims(dimensions_array), options)
|
|
67
66
|
elsif @orig_text.match(/-$/)
|
|
@@ -79,18 +78,29 @@ module Unitsml
|
|
|
79
78
|
Plurimath::Math.parse(to_mathml(options), :mathml)
|
|
80
79
|
end
|
|
81
80
|
|
|
81
|
+
def dimensions_extraction
|
|
82
|
+
extract_dimensions(value)
|
|
83
|
+
end
|
|
84
|
+
|
|
82
85
|
private
|
|
83
86
|
|
|
84
87
|
def extract_dimensions(formula)
|
|
85
88
|
formula.each_with_object([]) do |term, dimensions|
|
|
86
|
-
|
|
89
|
+
case term
|
|
90
|
+
when Dimension
|
|
87
91
|
dimensions << term
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
when Sqrt
|
|
93
|
+
if term.value.is_a?(Dimension)
|
|
94
|
+
sqrt_term = term.value.dup
|
|
95
|
+
sqrt_term.power_numerator = Number.new('0.5')
|
|
96
|
+
dimensions << sqrt_term
|
|
97
|
+
elsif term.value.is_a?(Fenced)
|
|
98
|
+
dimensions.concat(Array(term.value.dimensions_extraction))
|
|
99
|
+
end
|
|
100
|
+
when Formula
|
|
93
101
|
dimensions.concat(extract_dimensions(term.value))
|
|
102
|
+
when Fenced
|
|
103
|
+
dimensions.concat(Array(term.dimensions_extraction))
|
|
94
104
|
end
|
|
95
105
|
end
|
|
96
106
|
end
|
|
@@ -106,26 +116,28 @@ module Unitsml
|
|
|
106
116
|
next unless term.value.is_a?(Unit)
|
|
107
117
|
|
|
108
118
|
units_arr << term.value
|
|
119
|
+
when Fenced
|
|
120
|
+
units_arr.concat(extract_units([term.value]))
|
|
109
121
|
end
|
|
110
122
|
end
|
|
111
123
|
end
|
|
112
124
|
|
|
113
125
|
def units(options)
|
|
114
126
|
all_units = extract_units(value)
|
|
115
|
-
norm_text =
|
|
127
|
+
norm_text = all_units.map(&:xml_postprocess_name).join('*')
|
|
116
128
|
dims = Utility.units2dimensions(extract_units(value))
|
|
117
129
|
[
|
|
118
130
|
Utility.unit(all_units, self, dims, norm_text, explicit_value&.dig(:name), options),
|
|
119
131
|
Utility.prefixes(all_units, options),
|
|
120
132
|
*unique_dimensions(dims, norm_text),
|
|
121
|
-
Utility.quantity(norm_text, explicit_value&.dig(:quantity))
|
|
133
|
+
Utility.quantity(norm_text, explicit_value&.dig(:quantity))
|
|
122
134
|
].join
|
|
123
135
|
end
|
|
124
136
|
|
|
125
137
|
def unique_dimensions(dims, norm_text)
|
|
126
138
|
[
|
|
127
139
|
Utility.dimension(norm_text),
|
|
128
|
-
Utility.dimension_components(dims)
|
|
140
|
+
Utility.dimension_components(dims)
|
|
129
141
|
].uniq
|
|
130
142
|
end
|
|
131
143
|
|
|
@@ -133,31 +145,31 @@ module Unitsml
|
|
|
133
145
|
dim_id = dims.map(&:generate_id).join
|
|
134
146
|
attributes = { id: "D_#{dim_id}" }
|
|
135
147
|
dims.each { |dim| attributes.merge!(dim.xml_instances_hash(options)) }
|
|
136
|
-
Model::Dimension.new(attributes).to_xml
|
|
148
|
+
Model::Dimension.new(attributes).to_xml.force_encoding('UTF-8')
|
|
137
149
|
end
|
|
138
150
|
|
|
139
151
|
def sort_dims(values)
|
|
140
|
-
dims_hash = Utility::
|
|
152
|
+
dims_hash = Utility::DIM2D
|
|
141
153
|
values.sort do |first, second|
|
|
142
154
|
dims_hash.dig(first.dimension_name, :order) <=> dims_hash.dig(second.dimension_name, :order)
|
|
143
155
|
end
|
|
144
156
|
end
|
|
145
157
|
|
|
146
158
|
def prefixes(options)
|
|
147
|
-
norm_text = @norm_text&.split(
|
|
148
|
-
prefix_object = Unit.new(
|
|
159
|
+
norm_text = @norm_text&.split('-')&.first
|
|
160
|
+
prefix_object = Unit.new('', prefix: Prefix.new(norm_text))
|
|
149
161
|
[
|
|
150
162
|
Utility.prefixes([prefix_object], options),
|
|
151
163
|
Utility.dimension(norm_text),
|
|
152
|
-
Utility.quantity(norm_text, explicit_value&.dig(:quantity))
|
|
164
|
+
Utility.quantity(norm_text, explicit_value&.dig(:quantity))
|
|
153
165
|
].join
|
|
154
166
|
end
|
|
155
167
|
|
|
156
168
|
def ensure_plurimath_defined!
|
|
157
169
|
return if plurimath_available?
|
|
158
170
|
|
|
159
|
-
require
|
|
160
|
-
rescue LoadError
|
|
171
|
+
require 'plurimath'
|
|
172
|
+
rescue LoadError
|
|
161
173
|
raise Errors::PlurimathLoadError
|
|
162
174
|
end
|
|
163
175
|
|
|
@@ -166,7 +178,7 @@ module Unitsml
|
|
|
166
178
|
method_value = math_instance.public_send(:"#{method_name}_value") || []
|
|
167
179
|
method_value += Array(child_hash[:value])
|
|
168
180
|
math_instance.public_send(:"#{method_name}_value=", method_value)
|
|
169
|
-
math_instance.element_order << Lutaml::
|
|
181
|
+
math_instance.element_order << Lutaml::Xml::Element.new('Element', method_name.to_s)
|
|
170
182
|
end
|
|
171
183
|
|
|
172
184
|
def plurimath_available?
|
|
@@ -180,7 +192,7 @@ module Unitsml
|
|
|
180
192
|
end
|
|
181
193
|
|
|
182
194
|
def reset_mml_models
|
|
183
|
-
::Mml::Configuration.custom_models = Plurimath::Mathml::Parser::CONFIGURATION
|
|
195
|
+
::Mml::V4::Configuration.custom_models = Plurimath::Mathml::Parser::CONFIGURATION
|
|
184
196
|
end
|
|
185
197
|
|
|
186
198
|
def process_value(math, mathml_instances)
|
|
@@ -196,7 +208,8 @@ module Unitsml
|
|
|
196
208
|
return options unless root
|
|
197
209
|
|
|
198
210
|
multiplier = options[:multiplier] || explicit_value&.dig(:multiplier)
|
|
199
|
-
options.
|
|
211
|
+
explicit_parenthesis = options.key?(:explicit_parenthesis) ? options[:explicit_parenthesis] : true
|
|
212
|
+
options.merge(multiplier: multiplier, explicit_parenthesis: explicit_parenthesis).compact
|
|
200
213
|
end
|
|
201
214
|
end
|
|
202
215
|
end
|