asciimath2unitsml 0.3.1 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|