asciimath2unitsml 0.3.1 → 0.4.1
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/conv.rb +74 -119
- data/lib/asciimath2unitsml/dimensions.rb +117 -0
- data/lib/asciimath2unitsml/parse.rb +122 -133
- 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/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 +23 -19
@@ -1,144 +1,111 @@
|
|
1
1
|
module Asciimath2UnitsML
|
2
2
|
class Conv
|
3
3
|
include Rsec::Helpers
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
next if v.name.nil? || v.name.empty?
|
12
|
-
m[v.symbolid] = v
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def flip_name_and_symbols(hash)
|
17
|
-
hash.each_with_object({}) do |(k, v), m|
|
18
|
-
next if v.name.nil? || v.name.empty?
|
19
|
-
v.symbolids.each { |s| m[s] = v }
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def validate_yaml(hash, path)
|
24
|
-
return hash if path == "../unitsdb/quantities.yaml"
|
25
|
-
return hash if path == "../unitsdb/dimensions.yaml"
|
26
|
-
hash.each_with_object({}) do |(k, v), m|
|
27
|
-
path == "../unitsdb/units.yaml" and validate_unit(v)
|
28
|
-
m = validate_symbols(m, v)
|
29
|
-
v[:unit_symbols]&.each { |s| validate_unit_symbol_cardinality(s, k) }
|
30
|
-
end
|
31
|
-
hash
|
32
|
-
end
|
33
|
-
|
34
|
-
def validate_unit(v)
|
35
|
-
if v[:quantity_reference]
|
36
|
-
v[:quantity_reference].is_a?(Array) or
|
37
|
-
raise StandardError.new "No quantity_reference array provided for unit: #{v}"
|
38
|
-
end
|
39
|
-
if v[:unit_name]
|
40
|
-
v[:unit_name].is_a?(Array) or raise StandardError.new "No unit_name array provided for unit: #{v}"
|
41
|
-
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]
|
42
11
|
end
|
43
12
|
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
def validate_unit_symbol_cardinality(us, k)
|
56
|
-
return true if us.nil?
|
57
|
-
!us[:id].nil? && !us[:ascii].nil? && !us[:html].nil? && !us[:mathml].nil? && !us[:latex].nil? &&
|
58
|
-
!us[:unicode].nil? and return true
|
59
|
-
raise StandardError.new "malformed unit_symbol for #{k}: #{us}"
|
60
|
-
end
|
61
|
-
|
62
|
-
def symbol_key(v)
|
63
|
-
symbol = v[:unit_symbols]&.each_with_object([]) { |s, m| m << (s["id"] || s[:id]) } ||
|
64
|
-
v.dig(:symbol, :ascii) || v[:symbol] #|| v[:short]
|
65
|
-
symbol = [symbol] if !symbol.nil? && v[:unit_symbols] && !symbol.is_a?(Array)
|
66
|
-
symbol
|
67
|
-
end
|
68
|
-
|
69
|
-
def symbolize_keys(hash)
|
70
|
-
return hash if hash.is_a? String
|
71
|
-
hash.inject({}) do |result, (key, value)|
|
72
|
-
new_key = case key
|
73
|
-
when String then key.to_sym
|
74
|
-
else key
|
75
|
-
end
|
76
|
-
new_value = case value
|
77
|
-
when Hash then symbolize_keys(value)
|
78
|
-
when Array then value.map { |m| symbolize_keys(m) }
|
79
|
-
else value
|
80
|
-
end
|
81
|
-
result[new_key] = new_value
|
82
|
-
result
|
83
|
-
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
|
84
22
|
end
|
85
23
|
|
86
|
-
def
|
24
|
+
def units_parse(exponent, multiplier)
|
87
25
|
prefix2 = /#{@prefixes.keys.select { |x| x.size == 2 }.join("|")}/.r
|
88
26
|
prefix1 = /#{@prefixes.keys.select { |x| x.size == 1 }.join("|")}/.r
|
89
27
|
unit_keys = @units.keys.reject do |k|
|
90
28
|
/\*|\^|\/|^1$/.match(k) || @units[k].prefixed
|
91
29
|
end.map { |k| Regexp.escape(k) }
|
92
30
|
unit1 = /#{unit_keys.sort_by(&:length).reverse.join("|")}/.r
|
93
|
-
|
94
|
-
|
95
|
-
multiplier = %r{\*|//|/}.r.map { |x| { multiplier: x[0] } }
|
96
|
-
unit =
|
31
|
+
|
32
|
+
unit =
|
97
33
|
seq("sqrt(", unit1, ")") { |x| { prefix: nil, unit: x[1], display_exponent: "0.5" } } |
|
98
34
|
seq("sqrt(", prefix1, unit1, ")") { |x| { prefix: x[1], unit: x[2], display_exponent: "0.5" } } |
|
99
35
|
seq("sqrt(", prefix2, unit1, ")") { |x| { prefix: x[1], unit: x[2], display_exponent: "0.5" } } |
|
100
|
-
seq(unit1, exponent._? & (multiplier | ")".r)) { |x| { prefix: nil, unit: x[0], display_exponent: (x[1][0]
|
101
|
-
seq(unit1, exponent._?).eof { |x| { prefix: nil, unit: x[0], display_exponent: (x[1][0]
|
102
|
-
seq(prefix1, unit1, exponent._?
|
103
|
-
seq(prefix2, unit1, exponent._?
|
36
|
+
seq(unit1, exponent._? & (multiplier | ")".r)) { |x| { prefix: nil, unit: x[0], display_exponent: (x[1][0]) } } |
|
37
|
+
seq(unit1, exponent._?).eof { |x| { prefix: nil, unit: x[0], display_exponent: (x[1][0]) } } |
|
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]) } } |
|
104
40
|
"1".r.map { |_| { prefix: nil, unit: "1", display_exponent: nil } }
|
105
|
-
units1 = "(".r >> lazy{units} << ")" | unit
|
106
|
-
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
|
107
43
|
seq(prefix1, "-") { |x| [{ prefix: x[0], unit: nil, display_exponent: nil }] } |
|
108
44
|
units1.join(multiplier)
|
109
|
-
|
45
|
+
units
|
110
46
|
end
|
111
47
|
|
112
|
-
def parse(
|
113
|
-
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)
|
114
56
|
units = @parser.parse!(text[0])
|
115
57
|
if !units || Rsec::INVALID[units]
|
116
58
|
raise Rsec::SyntaxError.new "error parsing UnitsML expression", x, 1, 0
|
117
59
|
end
|
60
|
+
|
61
|
+
Rsec::Fail.reset
|
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
|
+
|
118
71
|
Rsec::Fail.reset
|
119
|
-
postprocess(units, text)
|
72
|
+
postprocess(units, text, false)
|
120
73
|
end
|
121
74
|
|
122
|
-
def postprocess(units, text)
|
75
|
+
def postprocess(units, text, is_units)
|
123
76
|
units = postprocess1(units.flatten)
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
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
|
131
88
|
end.join("*")
|
132
|
-
|
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]}" : ""
|
133
99
|
end
|
134
100
|
|
135
101
|
def postprocess1(units)
|
136
102
|
inverse = false
|
137
|
-
units.each_with_object([]) do |u, m|
|
103
|
+
units.each_with_object([]) do |u, m|
|
138
104
|
if u[:multiplier]
|
139
|
-
inverse = !inverse if
|
105
|
+
inverse = !inverse if u[:multiplier] == "/"
|
140
106
|
else
|
141
|
-
u[:exponent] =
|
107
|
+
u[:exponent] =
|
108
|
+
inverse ? "-#{u[:display_exponent] || '1'}" : u[:display_exponent]
|
142
109
|
u[:exponent] = u[:exponent]&.sub(/^--+/, "")
|
143
110
|
end
|
144
111
|
m << u
|
@@ -150,36 +117,53 @@ module Asciimath2UnitsML
|
|
150
117
|
MathML2UnitsML(xml).to_xml
|
151
118
|
end
|
152
119
|
|
153
|
-
# 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
|
154
122
|
def MathML2UnitsML(xml)
|
155
123
|
xml.is_a? String and xml = Nokogiri::XML(xml)
|
156
124
|
xml.xpath(".//m:mtext", "m" => MATHML_NS).each do |x|
|
157
|
-
next unless %r{^unitsml\(.+\)$}.match(x.text)
|
125
|
+
next unless %r{^unitsml\(.+\)$}.match?(x.text)
|
126
|
+
|
158
127
|
text = x.text.sub(%r{^unitsml\((.+)\)$}m, "\\1")
|
159
|
-
units, origtext, normtext, quantity, name, symbol, multiplier =
|
160
|
-
|
161
|
-
|
162
|
-
|
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
|
135
|
+
x.replace("#{delimspace(rendering, x)}"\
|
136
|
+
"<mrow xref='#{unit_id(origtext)}'>#{rendering}</mrow>\n"\
|
163
137
|
"#{unitsml(units, origtext, normtext, quantity, name)}")
|
164
138
|
end
|
165
139
|
dedup_ids(xml)
|
166
140
|
end
|
167
141
|
|
168
|
-
# 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
|
169
144
|
def delimspace(rendering, elem)
|
170
|
-
prec_text_elem =
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
145
|
+
prec_text_elem =
|
146
|
+
elem.xpath("./preceding-sibling::*[namespace-uri() = '#{MATHML_NS}']/"\
|
147
|
+
"descendant::text()[normalize-space()!='']"\
|
148
|
+
"[last()]/parent::*").last
|
149
|
+
return "" if prec_text_elem.nil? ||
|
150
|
+
!%w(mn mi).include?(prec_text_elem&.name)
|
151
|
+
|
152
|
+
text = HTMLEntities.new.encode(Nokogiri::XML("<mrow>#{rendering}</mrow>")
|
153
|
+
.text.strip)
|
154
|
+
if /\p{L}|\p{N}/.match?(text)
|
155
|
+
"<mo rspace='thickmathspace'>⁢</mo>"
|
156
|
+
else "<mo>⁢</mo>"
|
157
|
+
end
|
176
158
|
end
|
177
159
|
|
178
160
|
def dedup_ids(xml)
|
179
161
|
%w(Unit Dimension Prefix Quantity).each do |t|
|
180
|
-
xml.xpath(".//m:#{t}/@xml:id", "m" => UNITSML_NS).map
|
162
|
+
xml.xpath(".//m:#{t}/@xml:id", "m" => UNITSML_NS).map(&:text)
|
163
|
+
.uniq.each do |v|
|
181
164
|
xml.xpath(".//*[@xml:id = '#{v}']").each_with_index do |n, i|
|
182
|
-
next if i
|
165
|
+
next if i.zero?
|
166
|
+
|
183
167
|
n.remove
|
184
168
|
end
|
185
169
|
end
|
@@ -188,45 +172,50 @@ module Asciimath2UnitsML
|
|
188
172
|
end
|
189
173
|
|
190
174
|
def asciimath2mathml(expression)
|
191
|
-
AsciiMath::MathMLBuilder.new(:
|
192
|
-
AsciiMath.parse(HTMLEntities.new.decode(expression)).ast
|
193
|
-
gsub(/<math>/, "<math xmlns='#{MATHML_NS}'>")
|
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}'>")
|
194
178
|
end
|
195
179
|
|
196
180
|
def embeddedmathml(mathml)
|
197
181
|
x = Nokogiri::XML(mathml)
|
198
|
-
x.xpath(".//m:mi", "m" => MATHML_NS)
|
182
|
+
x.xpath(".//m:mi", "m" => MATHML_NS)
|
183
|
+
.each { |mi| mi["mathvariant"] = "normal" }
|
199
184
|
x.children.to_xml
|
200
185
|
end
|
201
186
|
|
202
187
|
def ambig_units
|
203
|
-
u = @units_id.each_with_object({}) do |(
|
188
|
+
u = @units_id.each_with_object({}) do |(_k, v), m|
|
204
189
|
v.symbolids.each do |x|
|
205
|
-
next if %r{[*/^]}.match(x)
|
190
|
+
next if %r{[*/^]}.match?(x)
|
206
191
|
next unless v.symbols_hash[x][:html] != x
|
192
|
+
|
207
193
|
m[v.symbols_hash[x][:html]] ||= []
|
208
194
|
m[v.symbols_hash[x][:html]] << x
|
209
195
|
end
|
210
196
|
end
|
211
|
-
u.
|
197
|
+
u.each_key { |k| u[k] = u[k].unshift(k) if @symbols.dig(k, :html) == k }
|
212
198
|
render_ambig_units(u)
|
213
199
|
end
|
214
200
|
|
215
201
|
def render_ambig_units(u)
|
216
202
|
maxcols = 0
|
217
203
|
u.each { |_, v| maxcols = v.size if maxcols < v.size }
|
218
|
-
puts %([cols="#{maxcols + 1}*"]\n|===\n|Symbol | Unit + ID #{
|
204
|
+
puts %([cols="#{maxcols + 1}*"]\n|===\n|Symbol | Unit + ID #{'| ' * (maxcols - 1)}\n)
|
219
205
|
puts "\n\n"
|
220
|
-
u.keys.sort_by
|
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|
|
221
210
|
print "| #{html2adoc(k)} "
|
222
|
-
u[k].sort_by
|
223
|
-
puts "#{
|
211
|
+
u[k].sort_by(&:size).each { |v1| print "| #{@units[v1].name}: `#{v1}` " }
|
212
|
+
puts "#{'| ' * (maxcols - u[k].size)}\n"
|
224
213
|
end
|
225
214
|
puts "|===\n"
|
226
215
|
end
|
227
216
|
|
228
|
-
def html2adoc(
|
229
|
-
|
217
|
+
def html2adoc(elem)
|
218
|
+
elem.gsub(%r{<i>}, "__").gsub(%r{</i>}, "__")
|
230
219
|
.gsub(%r{<sup>}, "^").gsub(%r{</sup>}, "^")
|
231
220
|
.gsub(%r{<sub>}, "~").gsub(%r{</sub>}, "~")
|
232
221
|
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
|