asciimath2unitsml 0.3.2 → 0.4.2
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 +15 -18
- data/bin/rspec +1 -2
- data/lib/asciimath2unitsml/conv.rb +32 -89
- data/lib/asciimath2unitsml/dimensions.rb +117 -0
- data/lib/asciimath2unitsml/parse.rb +93 -75
- 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 +27 -25
- data/lib/asciimath2unitsml/version.rb +1 -1
- data/lib/asciimath2unitsml.rb +0 -1
- data/lib/unitsdb/dimensions.yaml +65 -8
- data/lib/unitsdb_ruby/unitsdb.rb +87 -55
- data/spec/conv_spec.rb +1443 -1365
- data/spec/spec_helper.rb +10 -10
- metadata +28 -25
@@ -1,94 +1,101 @@
|
|
1
1
|
module Asciimath2UnitsML
|
2
2
|
class Conv
|
3
3
|
include Rsec::Helpers
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
hash.each_with_object({}) do |(_k, v), m|
|
12
|
-
next if v.name.nil? || v.name.empty?
|
13
|
-
|
14
|
-
m[v.symbolid] = v
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def flip_name_and_symbols(hash)
|
19
|
-
hash.each_with_object({}) do |(_k, v), m|
|
20
|
-
next if v.name.nil? || v.name.empty?
|
21
|
-
|
22
|
-
v.symbolids.each { |s| m[s] = v }
|
23
|
-
end
|
4
|
+
def parsers
|
5
|
+
exponent = /\^\(-?\d+\)/.r.map { |m| m.sub(/\^/, "").gsub(/[()]/, "") } |
|
6
|
+
/\^-?\d+/.r.map { |m| m.sub(/\^/, "") }
|
7
|
+
multiplier = %r{\*|//|/}.r.map { |x| { multiplier: x[0] } }
|
8
|
+
units = units_parse(exponent, multiplier)
|
9
|
+
dimensions = dimensions_parser(exponent, multiplier)
|
10
|
+
[units.eof, dimensions.eof]
|
24
11
|
end
|
25
12
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
when Hash then symbolize_keys(value)
|
36
|
-
when Array then value.map { |m| symbolize_keys(m) }
|
37
|
-
else value
|
38
|
-
end
|
39
|
-
result[new_key] = new_value
|
40
|
-
result
|
41
|
-
end
|
13
|
+
def dimensions_parser(exponent, multiplier)
|
14
|
+
dim1 = /#{@dimensions.keys.sort_by(&:length).reverse.join("|")}/.r
|
15
|
+
dimension =
|
16
|
+
seq("sqrt(", dim1, ")") { |x| { dim: x[1], display_exponent: "0.5" } } |
|
17
|
+
seq(dim1, exponent._? & (multiplier | ")".r)) { |x| { dim: x[0], display_exponent: (x[1][0]) } } |
|
18
|
+
seq(dim1, exponent._?).eof { |x| { dim: x[0], display_exponent: (x[1][0]) } }
|
19
|
+
dimensions1 = "(".r >> lazy { dimensions } << ")" | dimension
|
20
|
+
dimensions = dimensions1.join(multiplier) # rubocop:disable Style/RedundantAssignment
|
21
|
+
dimensions
|
42
22
|
end
|
43
23
|
|
44
|
-
def
|
24
|
+
def units_parse(exponent, multiplier)
|
45
25
|
prefix2 = /#{@prefixes.keys.select { |x| x.size == 2 }.join("|")}/.r
|
46
26
|
prefix1 = /#{@prefixes.keys.select { |x| x.size == 1 }.join("|")}/.r
|
47
27
|
unit_keys = @units.keys.reject do |k|
|
48
28
|
/\*|\^|\/|^1$/.match(k) || @units[k].prefixed
|
49
29
|
end.map { |k| Regexp.escape(k) }
|
50
30
|
unit1 = /#{unit_keys.sort_by(&:length).reverse.join("|")}/.r
|
51
|
-
|
52
|
-
|
53
|
-
multiplier = %r{\*|//|/}.r.map { |x| { multiplier: x[0] } }
|
54
|
-
unit =
|
31
|
+
|
32
|
+
unit =
|
55
33
|
seq("sqrt(", unit1, ")") { |x| { prefix: nil, unit: x[1], display_exponent: "0.5" } } |
|
56
34
|
seq("sqrt(", prefix1, unit1, ")") { |x| { prefix: x[1], unit: x[2], display_exponent: "0.5" } } |
|
57
35
|
seq("sqrt(", prefix2, unit1, ")") { |x| { prefix: x[1], unit: x[2], display_exponent: "0.5" } } |
|
58
36
|
seq(unit1, exponent._? & (multiplier | ")".r)) { |x| { prefix: nil, unit: x[0], display_exponent: (x[1][0]) } } |
|
59
37
|
seq(unit1, exponent._?).eof { |x| { prefix: nil, unit: x[0], display_exponent: (x[1][0]) } } |
|
60
|
-
seq(prefix1, unit1, exponent._?
|
61
|
-
seq(prefix2, unit1, exponent._?
|
38
|
+
seq(prefix1, unit1, exponent._?) { |x| { prefix: x[0], unit: x[1], display_exponent: (x[2][0]) } } |
|
39
|
+
seq(prefix2, unit1, exponent._?) { |x| { prefix: x[0], unit: x[1], display_exponent: (x[2][0]) } } |
|
62
40
|
"1".r.map { |_| { prefix: nil, unit: "1", display_exponent: nil } }
|
63
|
-
units1 = "(".r >> lazy{units} << ")" | unit
|
64
|
-
units = seq(prefix2, "-") { |x| [{ prefix: x[0], unit: nil, display_exponent: nil }] } |
|
41
|
+
units1 = "(".r >> lazy { units } << ")" | unit
|
42
|
+
units = seq(prefix2, "-") { |x| [{ prefix: x[0], unit: nil, display_exponent: nil }] } | # rubocop:disable Style/RedundantAssignment
|
65
43
|
seq(prefix1, "-") { |x| [{ prefix: x[0], unit: nil, display_exponent: nil }] } |
|
66
44
|
units1.join(multiplier)
|
67
|
-
|
45
|
+
units
|
68
46
|
end
|
69
47
|
|
70
|
-
def parse(
|
71
|
-
text = Array(
|
48
|
+
def parse(expr)
|
49
|
+
text = Array(expr.split(/,\s*/))
|
50
|
+
if /dim_/.match?(text[0]) then parse_dimensions(text)
|
51
|
+
else parse_units(text)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def parse_units(text)
|
72
56
|
units = @parser.parse!(text[0])
|
73
57
|
if !units || Rsec::INVALID[units]
|
74
58
|
raise Rsec::SyntaxError.new "error parsing UnitsML expression", x, 1, 0
|
75
59
|
end
|
76
60
|
|
77
61
|
Rsec::Fail.reset
|
78
|
-
postprocess(units, text)
|
62
|
+
postprocess(units, text, true)
|
63
|
+
end
|
64
|
+
|
65
|
+
def parse_dimensions(text)
|
66
|
+
units = @dim_parser.parse!(text[0])
|
67
|
+
if !units || Rsec::INVALID[units]
|
68
|
+
raise Rsec::SyntaxError.new "error parsing UnitsML expression", x, 1, 0
|
69
|
+
end
|
70
|
+
|
71
|
+
Rsec::Fail.reset
|
72
|
+
postprocess(units, text, false)
|
79
73
|
end
|
80
74
|
|
81
|
-
def postprocess(units, text)
|
75
|
+
def postprocess(units, text, is_units)
|
82
76
|
units = postprocess1(units.flatten)
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
77
|
+
normtext = postprocess_normtext(units, is_units)
|
78
|
+
[units, text[0], normtext, postprocess_extr(text, "quantity"),
|
79
|
+
postprocess_extr(text, "name"), postprocess_extr(text, "symbol"),
|
80
|
+
postprocess_extr(text, "multiplier")]
|
81
|
+
end
|
82
|
+
|
83
|
+
def postprocess_normtext(units, is_units)
|
84
|
+
units_only(units).each.map do |u|
|
85
|
+
if is_units then "#{u[:prefix]}#{u[:unit]}#{display_exp(u)}"
|
86
|
+
else "#{u[:dim]}#{display_exp(u)}"
|
87
|
+
end
|
90
88
|
end.join("*")
|
91
|
-
|
89
|
+
end
|
90
|
+
|
91
|
+
def postprocess_extr(text, name)
|
92
|
+
text[1..-1]&.select do |x|
|
93
|
+
/^#{name}:/.match(x)
|
94
|
+
end&.first&.sub(/^#{name}:\s*/, "")
|
95
|
+
end
|
96
|
+
|
97
|
+
def display_exp(unit)
|
98
|
+
unit[:exponent] && unit[:exponent] != "1" ? "^#{unit[:exponent]}" : ""
|
92
99
|
end
|
93
100
|
|
94
101
|
def postprocess1(units)
|
@@ -110,16 +117,21 @@ module Asciimath2UnitsML
|
|
110
117
|
MathML2UnitsML(xml).to_xml
|
111
118
|
end
|
112
119
|
|
113
|
-
# https://www.w3.org/TR/mathml-units/ section 2:
|
120
|
+
# https://www.w3.org/TR/mathml-units/ section 2:
|
121
|
+
# delimit number Invisible-Times unit
|
114
122
|
def MathML2UnitsML(xml)
|
115
123
|
xml.is_a? String and xml = Nokogiri::XML(xml)
|
116
124
|
xml.xpath(".//m:mtext", "m" => MATHML_NS).each do |x|
|
117
|
-
next unless %r{^unitsml\(.+\)$}.match(x.text)
|
125
|
+
next unless %r{^unitsml\(.+\)$}.match?(x.text)
|
118
126
|
|
119
127
|
text = x.text.sub(%r{^unitsml\((.+)\)$}m, "\\1")
|
120
|
-
units, origtext, normtext, quantity, name, symbol, multiplier =
|
121
|
-
|
122
|
-
|
128
|
+
units, origtext, normtext, quantity, name, symbol, multiplier =
|
129
|
+
parse(text)
|
130
|
+
rendering = if symbol
|
131
|
+
embeddedmathml(asciimath2mathml(symbol))
|
132
|
+
else
|
133
|
+
mathmlsymbol(units, false, multiplier)
|
134
|
+
end
|
123
135
|
x.replace("#{delimspace(rendering, x)}"\
|
124
136
|
"<mrow xref='#{unit_id(origtext)}'>#{rendering}</mrow>\n"\
|
125
137
|
"#{unitsml(units, origtext, normtext, quantity, name)}")
|
@@ -127,18 +139,22 @@ module Asciimath2UnitsML
|
|
127
139
|
dedup_ids(xml)
|
128
140
|
end
|
129
141
|
|
130
|
-
# if previous sibling's last descendent non-whitespace is MathML and
|
142
|
+
# if previous sibling's last descendent non-whitespace is MathML and
|
143
|
+
# mn or mi, no space
|
131
144
|
def delimspace(rendering, elem)
|
132
145
|
prec_text_elem =
|
133
146
|
elem.xpath("./preceding-sibling::*[namespace-uri() = '#{MATHML_NS}']/"\
|
134
|
-
"descendant::text()[normalize-space()!='']
|
147
|
+
"descendant::text()[normalize-space()!='']"\
|
148
|
+
"[last()]/parent::*").last
|
135
149
|
return "" if prec_text_elem.nil? ||
|
136
150
|
!%w(mn mi).include?(prec_text_elem&.name)
|
137
151
|
|
138
152
|
text = HTMLEntities.new.encode(Nokogiri::XML("<mrow>#{rendering}</mrow>")
|
139
153
|
.text.strip)
|
140
|
-
/\p{L}|\p{N}/.match(text)
|
141
|
-
"<mo rspace='thickmathspace'>⁢</mo>"
|
154
|
+
if /\p{L}|\p{N}/.match?(text)
|
155
|
+
"<mo rspace='thickmathspace'>⁢</mo>"
|
156
|
+
else "<mo>⁢</mo>"
|
157
|
+
end
|
142
158
|
end
|
143
159
|
|
144
160
|
def dedup_ids(xml)
|
@@ -156,9 +172,9 @@ module Asciimath2UnitsML
|
|
156
172
|
end
|
157
173
|
|
158
174
|
def asciimath2mathml(expression)
|
159
|
-
AsciiMath::MathMLBuilder.new(:
|
160
|
-
AsciiMath.parse(HTMLEntities.new.decode(expression)).ast
|
161
|
-
|
175
|
+
AsciiMath::MathMLBuilder.new(msword: true).append_expression(
|
176
|
+
AsciiMath.parse(HTMLEntities.new.decode(expression)).ast,
|
177
|
+
).to_s.gsub(/<math>/, "<math xmlns='#{MATHML_NS}'>")
|
162
178
|
end
|
163
179
|
|
164
180
|
def embeddedmathml(mathml)
|
@@ -171,7 +187,7 @@ module Asciimath2UnitsML
|
|
171
187
|
def ambig_units
|
172
188
|
u = @units_id.each_with_object({}) do |(_k, v), m|
|
173
189
|
v.symbolids.each do |x|
|
174
|
-
next if %r{[*/^]}.match(x)
|
190
|
+
next if %r{[*/^]}.match?(x)
|
175
191
|
next unless v.symbols_hash[x][:html] != x
|
176
192
|
|
177
193
|
m[v.symbols_hash[x][:html]] ||= []
|
@@ -185,13 +201,15 @@ module Asciimath2UnitsML
|
|
185
201
|
def render_ambig_units(u)
|
186
202
|
maxcols = 0
|
187
203
|
u.each { |_, v| maxcols = v.size if maxcols < v.size }
|
188
|
-
puts %([cols="#{maxcols + 1}*"]\n|===\n|Symbol | Unit + ID #{
|
204
|
+
puts %([cols="#{maxcols + 1}*"]\n|===\n|Symbol | Unit + ID #{'| ' * (maxcols - 1)}\n)
|
189
205
|
puts "\n\n"
|
190
|
-
u.keys.sort_by
|
191
|
-
.gsub(
|
206
|
+
u.keys.sort_by do |a|
|
207
|
+
[-u[a].size, a.gsub(%r{&[^;]+;}, "")
|
208
|
+
.gsub(/[^A-Za-z]/, "").downcase]
|
209
|
+
end.each do |k|
|
192
210
|
print "| #{html2adoc(k)} "
|
193
211
|
u[k].sort_by(&:size).each { |v1| print "| #{@units[v1].name}: `#{v1}` " }
|
194
|
-
puts "#{'| ' * (maxcols - u[k].size)
|
212
|
+
puts "#{'| ' * (maxcols - u[k].size)}\n"
|
195
213
|
end
|
196
214
|
puts "|===\n"
|
197
215
|
end
|
@@ -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
|
@@ -12,44 +12,46 @@ module Asciimath2UnitsML
|
|
12
12
|
hash
|
13
13
|
end
|
14
14
|
|
15
|
-
def validate_unit(
|
16
|
-
if
|
17
|
-
|
18
|
-
raise StandardError
|
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}"
|
19
20
|
end
|
20
|
-
if
|
21
|
-
|
22
|
-
raise StandardError
|
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}"
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
26
|
-
def validate_symbols(
|
27
|
-
symbol = symbol_key(
|
28
|
+
def validate_symbols(acc, val)
|
29
|
+
symbol = symbol_key(val)
|
28
30
|
!symbol.nil? or
|
29
|
-
raise StandardError.new "No symbol provided for unit: #{
|
31
|
+
raise StandardError.new "No symbol provided for unit: #{val}"
|
30
32
|
Array(symbol)&.each do |s|
|
31
|
-
|
32
|
-
raise StandardError.new "symbol #{s} is not unique in #{
|
33
|
-
"already used for #{
|
34
|
-
|
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
|
35
37
|
end
|
36
|
-
|
38
|
+
acc
|
37
39
|
end
|
38
40
|
|
39
|
-
def validate_unit_symbol_cardinality(
|
40
|
-
return true if
|
41
|
+
def validate_unit_symbol_cardinality(sym, key)
|
42
|
+
return true if sym.nil?
|
41
43
|
|
42
|
-
!
|
43
|
-
!
|
44
|
-
!
|
45
|
-
raise StandardError.new "malformed unit_symbol for #{
|
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}"
|
46
48
|
end
|
47
49
|
|
48
|
-
def symbol_key(
|
49
|
-
symbol =
|
50
|
+
def symbol_key(val)
|
51
|
+
symbol = val[:unit_symbols]&.each_with_object([]) do |s, m|
|
50
52
|
m << (s["id"] || s[:id])
|
51
|
-
end ||
|
52
|
-
!symbol.nil? &&
|
53
|
+
end || val.dig(:symbol, :ascii) || val[:symbol] # || val[:short]
|
54
|
+
!symbol.nil? && val[:unit_symbols] && !symbol.is_a?(Array) and
|
53
55
|
symbol = [symbol]
|
54
56
|
symbol
|
55
57
|
end
|
data/lib/asciimath2unitsml.rb
CHANGED