asciimath2unitsml 0.3.0 → 0.4.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 +2 -23
- data/.hound.yml +5 -0
- data/.rubocop.yml +4 -8
- data/Gemfile +1 -1
- data/README.adoc +19 -1
- data/Rakefile +1 -1
- data/asciimath2unitsml.gemspec +14 -17
- data/bin/rspec +1 -2
- data/lib/asciimath2unitsml.rb +0 -1
- data/lib/asciimath2unitsml/conv.rb +74 -119
- data/lib/asciimath2unitsml/dimensions.rb +117 -0
- data/lib/asciimath2unitsml/parse.rb +127 -137
- data/lib/asciimath2unitsml/read.rb +42 -0
- data/lib/asciimath2unitsml/render.rb +48 -31
- data/lib/asciimath2unitsml/unit.rb +29 -23
- data/lib/asciimath2unitsml/validate.rb +59 -0
- data/lib/asciimath2unitsml/version.rb +1 -1
- data/lib/unitsdb/dimensions.yaml +65 -8
- data/lib/unitsdb_ruby/unitsdb.rb +87 -55
- data/spec/conv_spec.rb +1447 -1227
- data/spec/spec_helper.rb +10 -10
- metadata +23 -19
@@ -0,0 +1,42 @@
|
|
1
|
+
module Asciimath2UnitsML
|
2
|
+
class Conv
|
3
|
+
def read_yaml(path)
|
4
|
+
validate_yaml(symbolize_keys(YAML
|
5
|
+
.load_file(File.join(File.join(File.dirname(__FILE__), path)))), path)
|
6
|
+
end
|
7
|
+
|
8
|
+
def symbolize_keys(hash)
|
9
|
+
return hash if hash.is_a? String
|
10
|
+
|
11
|
+
hash.inject({}) do |result, (key, value)|
|
12
|
+
new_key = case key
|
13
|
+
when String then key.to_sym
|
14
|
+
else key
|
15
|
+
end
|
16
|
+
new_value = case value
|
17
|
+
when Hash then symbolize_keys(value)
|
18
|
+
when Array then value.map { |m| symbolize_keys(m) }
|
19
|
+
else value
|
20
|
+
end
|
21
|
+
result[new_key] = new_value
|
22
|
+
result
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def flip_name_and_symbol(hash)
|
27
|
+
hash.each_with_object({}) do |(_k, v), m|
|
28
|
+
next if v.name.nil? || v.name.empty?
|
29
|
+
|
30
|
+
m[v.symbolid] = v
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def flip_name_and_symbols(hash)
|
35
|
+
hash.each_with_object({}) do |(_k, v), m|
|
36
|
+
next if v.name.nil? || v.name.empty?
|
37
|
+
|
38
|
+
v.symbolids.each { |s| m[s] = v }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -1,13 +1,14 @@
|
|
1
1
|
module Asciimath2UnitsML
|
2
2
|
class Conv
|
3
|
-
def multiplier(
|
4
|
-
case
|
3
|
+
def multiplier(val)
|
4
|
+
case val
|
5
5
|
when :space
|
6
6
|
{ html: " ", mathml: "<mo rspace='thickmathspace'>⁢</mo>" }
|
7
7
|
when :nospace
|
8
8
|
{ html: "", mathml: "<mo>⁢</mo>" }
|
9
9
|
else
|
10
|
-
{ html: HTMLEntities.new.encode(
|
10
|
+
{ html: HTMLEntities.new.encode(val),
|
11
|
+
mathml: "<mo>#{HTMLEntities.new.encode(val)}</mo>" }
|
11
12
|
end
|
12
13
|
end
|
13
14
|
|
@@ -15,28 +16,31 @@ module Asciimath2UnitsML
|
|
15
16
|
@symbols[unit][style] || unit
|
16
17
|
end
|
17
18
|
|
18
|
-
def htmlent(
|
19
|
-
HTMLEntities.new.decode(
|
20
|
-
|
19
|
+
def htmlent(xml)
|
20
|
+
HTMLEntities.new.decode(xml).split(/([<>&])/).map do |c|
|
21
|
+
/[<>'"]/.match?(c) ? c : HTMLEntities.new.encode(c, :hexadecimal)
|
22
|
+
end.join
|
21
23
|
end
|
22
24
|
|
23
25
|
def htmlsymbol(units, normalise)
|
24
26
|
units.map do |u|
|
25
|
-
if u[:multiplier]
|
27
|
+
if u[:multiplier]
|
28
|
+
u[:multiplier] == "*" ? @multiplier[:html] : u[:multiplier]
|
26
29
|
elsif u[:unit].nil? && u[:prefix]
|
27
30
|
@prefixes[u[:prefix]].html
|
28
31
|
else
|
29
|
-
base = (u[:prefix] || "") +
|
32
|
+
base = (u[:prefix] || "") +
|
33
|
+
render(normalise ? @units[u[:unit]].symbolid : u[:unit], :html)
|
30
34
|
htmlsymbol_exponent(u, base)
|
31
35
|
end
|
32
|
-
end.join
|
36
|
+
end.join
|
33
37
|
end
|
34
38
|
|
35
|
-
def htmlsymbol_exponent(
|
36
|
-
if
|
39
|
+
def htmlsymbol_exponent(unit, base)
|
40
|
+
if unit[:display_exponent] == "0.5"
|
37
41
|
base = "√#{base}"
|
38
|
-
elsif
|
39
|
-
exp = "<sup>#{
|
42
|
+
elsif unit[:display_exponent]
|
43
|
+
exp = "<sup>#{unit[:display_exponent].sub(/-/, '−')}</sup>"
|
40
44
|
base += exp
|
41
45
|
end
|
42
46
|
base
|
@@ -44,41 +48,54 @@ module Asciimath2UnitsML
|
|
44
48
|
|
45
49
|
def mathmlsymbol(units, normalise, multiplier = nil)
|
46
50
|
multiplier = multiplier ? "<mo>#{multiplier}</mo>" : @multiplier[:mathml]
|
47
|
-
|
48
|
-
if u[:multiplier]
|
51
|
+
units.map do |u|
|
52
|
+
if u[:multiplier]
|
53
|
+
u[:multiplier] == "*" ? multiplier : "<mo>#{u[:multiplier]}</mo>"
|
49
54
|
elsif u[:unit].nil? && u[:prefix]
|
50
55
|
%(<mi mathvariant='normal'>#{htmlent(@prefixes[u[:prefix]].html)}</mi>)
|
51
56
|
else
|
52
57
|
mathmlsymbol1(u, normalise)
|
53
58
|
end
|
54
|
-
end.join
|
59
|
+
end.join
|
60
|
+
end
|
61
|
+
|
62
|
+
def mathmlsymbol1(unit, normalise)
|
63
|
+
base = if unit[:dim]
|
64
|
+
render(normalise ? @dimensions[unit[:dim]].symbolid : unit[:dim],
|
65
|
+
:mathml)
|
66
|
+
else
|
67
|
+
render(normalise ? @units[unit[:unit]].symbolid : unit[:unit],
|
68
|
+
:mathml)
|
69
|
+
end
|
70
|
+
unit[:prefix] and base = mathmlsymbol1_prefixed(unit, base)
|
71
|
+
mathmlsymbol_exponent(unit, base)
|
55
72
|
end
|
56
73
|
|
57
|
-
def
|
58
|
-
|
59
|
-
if
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
74
|
+
def mathmlsymbol1_prefixed(unit, base)
|
75
|
+
prefix = htmlent(@prefixes[unit[:prefix]].html)
|
76
|
+
if /<mi mathvariant='normal'>/.match?(base)
|
77
|
+
base.sub(/<mi mathvariant='normal'>/,
|
78
|
+
"<mi mathvariant='normal'>#{prefix}")
|
79
|
+
else
|
80
|
+
"<mrow><mi mathvariant='normal'>#{prefix}#{base}</mrow>"
|
64
81
|
end
|
65
|
-
mathmlsymbol_exponent(u, base)
|
66
82
|
end
|
67
83
|
|
68
|
-
def mathmlsymbol_exponent(
|
69
|
-
if
|
84
|
+
def mathmlsymbol_exponent(unit, base)
|
85
|
+
if unit[:display_exponent] == "0.5"
|
70
86
|
base = "<msqrt>#{base}</msqrt>"
|
71
|
-
elsif
|
72
|
-
exp = "<mn>#{
|
87
|
+
elsif unit[:display_exponent]
|
88
|
+
exp = "<mn>#{unit[:display_exponent]}</mn>"
|
89
|
+
.sub(/<mn>-/, "<mo>−</mo><mn>")
|
73
90
|
base = "<msup><mrow>#{base}</mrow><mrow>#{exp}</mrow></msup>"
|
74
91
|
end
|
75
92
|
base
|
76
93
|
end
|
77
94
|
|
78
95
|
def mathmlsymbolwrap(units, normalise)
|
79
|
-
<<~
|
80
|
-
|
81
|
-
|
96
|
+
<<~XML
|
97
|
+
<math xmlns='#{MATHML_NS}'><mrow>#{mathmlsymbol(units, normalise)}</mrow></math>
|
98
|
+
XML
|
82
99
|
end
|
83
100
|
end
|
84
101
|
end
|
@@ -7,21 +7,22 @@ module Asciimath2UnitsML
|
|
7
7
|
def unit_id(text)
|
8
8
|
text = text.gsub(/[()]/, "")
|
9
9
|
/-$/.match(text) and return @prefixes[text.sub(/-$/, "")].id
|
10
|
-
"U_
|
10
|
+
"U_#{@units[text] ? @units[text].id.gsub(/'/, '_') : text.gsub(/\*/, '.').gsub(/\^/, '')}"
|
11
11
|
end
|
12
12
|
|
13
|
-
def unit(units,
|
13
|
+
def unit(units, _origtext, normtext, dims, name)
|
14
14
|
return if units_only(units).any? { |x| x[:unit].nil? }
|
15
|
+
|
15
16
|
dimid = dim_id(dims)
|
16
17
|
norm_units = normalise_units(units)
|
17
|
-
<<~
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
18
|
+
<<~XML
|
19
|
+
<Unit xmlns='#{UNITSML_NS}' xml:id='#{unit_id(normtext)}'#{dimid ? " dimensionURL='##{dimid}'" : ''}>
|
20
|
+
#{unitsystem(units)}
|
21
|
+
#{unitname(norm_units, normtext, name)}
|
22
|
+
#{unitsymbol(norm_units)}
|
23
|
+
#{rootunits(units)}
|
24
|
+
</Unit>
|
25
|
+
XML
|
25
26
|
end
|
26
27
|
|
27
28
|
def normalise_units(units)
|
@@ -36,14 +37,17 @@ module Asciimath2UnitsML
|
|
36
37
|
# kg exception
|
37
38
|
def unitsystem(units)
|
38
39
|
return if units_only(units).any? { |x| x[:unit].nil? }
|
40
|
+
|
39
41
|
ret = []
|
40
42
|
units = units_only(units)
|
41
43
|
units.any? { |x| @units[x[:unit]].system_name != "SI" } and
|
42
44
|
ret << "<UnitSystem name='not_SI' type='not_SI' xml:lang='en-US'/>"
|
43
45
|
if units.any? { |x| @units[x[:unit]].system_name == "SI" }
|
44
|
-
base = units.size == 1 &&
|
45
|
-
|
46
|
-
|
46
|
+
base = units.size == 1 &&
|
47
|
+
@units[units[0][:unit]].system_type == "SI-base"
|
48
|
+
base = true if units.size == 1 && units[0][:unit] == "g" &&
|
49
|
+
units[0][:prefix] == "k"
|
50
|
+
ret << "<UnitSystem name='SI' type='#{base ? 'SI_base' : 'SI_derived'}' xml:lang='en-US'/>"
|
47
51
|
end
|
48
52
|
ret.join("\n")
|
49
53
|
end
|
@@ -54,28 +58,30 @@ module Asciimath2UnitsML
|
|
54
58
|
end
|
55
59
|
|
56
60
|
# TODO: compose name from the component units
|
57
|
-
def compose_name(
|
61
|
+
def compose_name(_units, text)
|
58
62
|
text
|
59
63
|
end
|
60
64
|
|
61
65
|
def unitsymbol(units)
|
62
|
-
<<~
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
+
<<~XML
|
67
|
+
<UnitSymbol type="HTML">#{htmlsymbol(units, true)}</UnitSymbol>
|
68
|
+
<UnitSymbol type="MathML">#{mathmlsymbolwrap(units, true)}</UnitSymbol>
|
69
|
+
XML
|
66
70
|
end
|
67
71
|
|
68
72
|
def rootunits(units)
|
69
73
|
return if units_only(units).any? { |x| x[:unit].nil? }
|
70
74
|
return if units.size == 1 && !units[0][:prefix]
|
75
|
+
|
71
76
|
exp = units_only(units).map do |u|
|
72
77
|
prefix = " prefix='#{u[:prefix]}'" if u[:prefix]
|
73
|
-
|
74
|
-
|
78
|
+
u[:exponent] && u[:exponent] != "1" and
|
79
|
+
arg = " powerNumerator='#{u[:exponent]}'"
|
80
|
+
"<EnumeratedRootUnit unit='#{@units[u[:unit]].name}'#{prefix}#{arg}/>"
|
75
81
|
end.join("\n")
|
76
|
-
<<~
|
77
|
-
|
78
|
-
|
82
|
+
<<~XML
|
83
|
+
<RootUnits>#{exp}</RootUnits>
|
84
|
+
XML
|
79
85
|
end
|
80
86
|
end
|
81
87
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Asciimath2UnitsML
|
2
|
+
class Conv
|
3
|
+
def validate_yaml(hash, path)
|
4
|
+
return hash if path == "../unitsdb/quantities.yaml"
|
5
|
+
return hash if path == "../unitsdb/dimensions.yaml"
|
6
|
+
|
7
|
+
hash.each_with_object({}) do |(k, v), m|
|
8
|
+
path == "../unitsdb/units.yaml" and validate_unit(v)
|
9
|
+
m = validate_symbols(m, v)
|
10
|
+
v[:unit_symbols]&.each { |s| validate_unit_symbol_cardinality(s, k) }
|
11
|
+
end
|
12
|
+
hash
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate_unit(unit)
|
16
|
+
if unit[:quantity_reference]
|
17
|
+
unit[:quantity_reference].is_a?(Array) or
|
18
|
+
raise StandardError
|
19
|
+
.new "No quantity_reference array provided for unit: #{unit}"
|
20
|
+
end
|
21
|
+
if unit[:unit_name]
|
22
|
+
unit[:unit_name].is_a?(Array) or
|
23
|
+
raise StandardError
|
24
|
+
.new "No unit_name array provided for unit: #{unit}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate_symbols(acc, val)
|
29
|
+
symbol = symbol_key(val)
|
30
|
+
!symbol.nil? or
|
31
|
+
raise StandardError.new "No symbol provided for unit: #{val}"
|
32
|
+
Array(symbol)&.each do |s|
|
33
|
+
acc[s] && s != "1" and
|
34
|
+
raise StandardError.new "symbol #{s} is not unique in #{val}: "\
|
35
|
+
"already used for #{acc[s]}"
|
36
|
+
acc[s] = val
|
37
|
+
end
|
38
|
+
acc
|
39
|
+
end
|
40
|
+
|
41
|
+
def validate_unit_symbol_cardinality(sym, key)
|
42
|
+
return true if sym.nil?
|
43
|
+
|
44
|
+
!sym[:id].nil? && !sym[:ascii].nil? && !sym[:html].nil? &&
|
45
|
+
!sym[:mathml].nil? && !sym[:latex].nil? &&
|
46
|
+
!sym[:unicode].nil? and return true
|
47
|
+
raise StandardError.new "malformed unit_symbol for #{key}: #{sym}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def symbol_key(val)
|
51
|
+
symbol = val[:unit_symbols]&.each_with_object([]) do |s, m|
|
52
|
+
m << (s["id"] || s[:id])
|
53
|
+
end || val.dig(:symbol, :ascii) || val[:symbol] # || val[:short]
|
54
|
+
!symbol.nil? && val[:unit_symbols] && !symbol.is_a?(Array) and
|
55
|
+
symbol = [symbol]
|
56
|
+
symbol
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/unitsdb/dimensions.yaml
CHANGED
@@ -3,36 +3,86 @@ NISTd1:
|
|
3
3
|
length:
|
4
4
|
powerNumerator: 1
|
5
5
|
symbol: L
|
6
|
+
dim_symbols:
|
7
|
+
- id: "dim_L"
|
8
|
+
ascii: "L"
|
9
|
+
html: "𝖫"
|
10
|
+
mathml: "<mi mathvariant='sans-serif'>L</mi>"
|
11
|
+
latex: \ensuremath{\mathsf{L}}
|
12
|
+
unicode: "𝖫"
|
13
|
+
|
6
14
|
|
7
15
|
NISTd2:
|
8
16
|
mass:
|
9
17
|
powerNumerator: 1
|
10
18
|
symbol: M
|
19
|
+
dim_symbols:
|
20
|
+
- id: "dim_M"
|
21
|
+
ascii: "M"
|
22
|
+
html: "𝖬"
|
23
|
+
mathml: "<mi mathvariant='sans-serif'>M</mi>"
|
24
|
+
latex: \ensuremath{\mathsf{M}}
|
25
|
+
unicode: "𝖬"
|
11
26
|
|
12
27
|
NISTd3:
|
13
28
|
time:
|
14
29
|
powerNumerator: 1
|
15
30
|
symbol: T
|
31
|
+
dim_symbols:
|
32
|
+
- id: "dim_T"
|
33
|
+
ascii: "T"
|
34
|
+
html: "𝖳"
|
35
|
+
mathml: "<mi mathvariant='sans-serif'>T</mi>"
|
36
|
+
latex: \ensuremath{\mathsf{T}}
|
37
|
+
unicode: "𝖳"
|
16
38
|
|
17
39
|
NISTd4:
|
18
40
|
electric_current:
|
19
41
|
powerNumerator: 1
|
20
42
|
symbol: I
|
43
|
+
dim_symbols:
|
44
|
+
- id: "dim_T"
|
45
|
+
ascii: "T"
|
46
|
+
html: "𝖨"
|
47
|
+
mathml: "<mi mathvariant='sans-serif'>T</mi>"
|
48
|
+
latex: \ensuremath{\mathsf{T}}
|
49
|
+
unicode: "𝖨"
|
21
50
|
|
22
51
|
NISTd5:
|
23
52
|
thermodynamic_temperature:
|
24
53
|
powerNumerator: 1
|
25
|
-
symbol:
|
54
|
+
symbol: Theta
|
55
|
+
dim_symbols:
|
56
|
+
- id: "dim_Theta"
|
57
|
+
ascii: "Theta"
|
58
|
+
html: "𝝠"
|
59
|
+
mathml: "<mi mathvariant='sans-serif'>Θ</mi>"
|
60
|
+
latex: \ensuremath{\mathsf{\Theta}}
|
61
|
+
unicode: "𝝧"
|
26
62
|
|
27
63
|
NISTd6:
|
28
64
|
amount_of_substance:
|
29
65
|
powerNumerator: 1
|
30
66
|
symbol: N
|
67
|
+
dim_symbols:
|
68
|
+
- id: "dim_N"
|
69
|
+
ascii: "N"
|
70
|
+
html: "𝖭"
|
71
|
+
mathml: "<mi mathvariant='sans-serif'>N</mi>"
|
72
|
+
latex: \ensuremath{\mathsf{N}}
|
73
|
+
unicode: "𝖭"
|
31
74
|
|
32
75
|
NISTd7:
|
33
76
|
luminous_intensity:
|
34
77
|
powerNumerator: 1
|
35
78
|
symbol: J
|
79
|
+
dim_symbols:
|
80
|
+
- id: "dim_J"
|
81
|
+
ascii: "J"
|
82
|
+
html: "𝖩"
|
83
|
+
mathml: "<mi mathvariant='sans-serif'>J</mi>"
|
84
|
+
latex: \ensuremath{\mathsf{J}}
|
85
|
+
unicode: "𝖩"
|
36
86
|
|
37
87
|
NISTd8:
|
38
88
|
length:
|
@@ -44,6 +94,13 @@ NISTd9:
|
|
44
94
|
plane_angle:
|
45
95
|
powerNumerator: 1
|
46
96
|
symbol: phi
|
97
|
+
dim_symbols:
|
98
|
+
- id: "dim_phi"
|
99
|
+
ascii: "phi"
|
100
|
+
html: "𝞅"
|
101
|
+
mathml: "<mi mathvariant='sans-serif'>φ</mi>"
|
102
|
+
latex: \ensuremath{\mathsf{\phi}}
|
103
|
+
unicode: "𝞅"
|
47
104
|
|
48
105
|
NISTd10:
|
49
106
|
length:
|
@@ -312,7 +369,7 @@ NISTd39:
|
|
312
369
|
symbol: T
|
313
370
|
thermodynamic_temperature:
|
314
371
|
powerNumerator: -1
|
315
|
-
symbol:
|
372
|
+
symbol: Theta
|
316
373
|
|
317
374
|
NISTd36:
|
318
375
|
length:
|
@@ -367,7 +424,7 @@ NISTd40:
|
|
367
424
|
symbol: T
|
368
425
|
thermodynamic_temperature:
|
369
426
|
powerNumerator: -1
|
370
|
-
symbol:
|
427
|
+
symbol: Theta
|
371
428
|
|
372
429
|
NISTd41:
|
373
430
|
length:
|
@@ -381,7 +438,7 @@ NISTd41:
|
|
381
438
|
symbol: T
|
382
439
|
thermodynamic_temperature:
|
383
440
|
powerNumerator: -1
|
384
|
-
symbol:
|
441
|
+
symbol: Theta
|
385
442
|
|
386
443
|
NISTd46:
|
387
444
|
length:
|
@@ -475,7 +532,7 @@ NISTd48:
|
|
475
532
|
symbol: T
|
476
533
|
thermodynamic_temperature:
|
477
534
|
powerNumerator: -1
|
478
|
-
symbol:
|
535
|
+
symbol: Theta
|
479
536
|
amount_of_substance:
|
480
537
|
powerNumerator: -1
|
481
538
|
symbol: N
|
@@ -603,7 +660,7 @@ NISTd60:
|
|
603
660
|
NISTd68:
|
604
661
|
thermodynamic_temperature:
|
605
662
|
powerNumerator: -1
|
606
|
-
symbol:
|
663
|
+
symbol: Theta
|
607
664
|
|
608
665
|
NISTd69:
|
609
666
|
length:
|
@@ -617,7 +674,7 @@ NISTd69:
|
|
617
674
|
symbol: T
|
618
675
|
thermodynamic_temperature:
|
619
676
|
powerNumerator: -1
|
620
|
-
symbol:
|
677
|
+
symbol: Theta
|
621
678
|
|
622
679
|
NISTd70:
|
623
680
|
length:
|
@@ -639,7 +696,7 @@ NISTd71:
|
|
639
696
|
symbol: T
|
640
697
|
thermodynamic_temperature:
|
641
698
|
powerNumerator: -1
|
642
|
-
symbol:
|
699
|
+
symbol: Theta
|
643
700
|
|
644
701
|
NISTd64:
|
645
702
|
dimensionless: true
|