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.
@@ -1,144 +1,111 @@
1
1
  module Asciimath2UnitsML
2
2
  class Conv
3
3
  include Rsec::Helpers
4
-
5
- def read_yaml(path)
6
- validate_yaml(symbolize_keys(YAML.load_file(File.join(File.join(File.dirname(__FILE__), path)))), path)
7
- end
8
-
9
- def flip_name_and_symbol(hash)
10
- hash.each_with_object({}) do |(k, v), m|
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 validate_symbols(m, v)
45
- symbol = symbol_key(v)
46
- !symbol.nil? or raise StandardError.new "No symbol provided for unit: #{v}"
47
- Array(symbol)&.each do |s|
48
- m[s] && s != "1" and
49
- raise StandardError.new "symbol #{s} is not unique in #{v}: already used for #{m[s]}"
50
- m[s] = v
51
- end
52
- m
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 parser
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
- exponent = /\^\(-?\d+\)/.r.map { |m| m.sub(/\^/, "").gsub(/[()]/, "") } |
94
- /\^-?\d+/.r.map { |m| m.sub(/\^/, "") }
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._? ) { |x| { prefix: x[0], unit: x[1], display_exponent: (x[2][0] ) } } |
103
- seq(prefix2, unit1, exponent._? ) { |x| { prefix: x[0], unit: x[1], display_exponent: (x[2][0] ) } } |
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
- parser = units.eof
45
+ units
110
46
  end
111
47
 
112
- def parse(x)
113
- text = Array(x.split(/,\s*/))
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
- quantity = text[1..-1]&.select { |x| /^quantity:/.match(x) }&.first&.sub(/^quantity:\s*/, "")
125
- name = text[1..-1]&.select { |x| /^name:/.match(x) }&.first&.sub(/^name:\s*/, "")
126
- symbol = text[1..-1]&.select { |x| /^symbol:/.match(x) }&.first&.sub(/^symbol:\s*/, "")
127
- multiplier = text[1..-1]&.select { |x| /^multiplier:/.match(x) }&.first&.sub(/^multiplier:\s*/, "")
128
- normtext = units_only(units).each.map do |u|
129
- exp = u[:exponent] && u[:exponent] != "1" ? "^#{u[:exponent]}" : ""
130
- "#{u[:prefix]}#{u[:unit]}#{exp}"
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
- [units, text[0], normtext, quantity, name, symbol, multiplier]
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 (u[:multiplier] == "/")
105
+ inverse = !inverse if u[:multiplier] == "/"
140
106
  else
141
- u[:exponent] = inverse ? "-#{u[:display_exponent] || '1'}" : u[:display_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: delimit number Invisible-Times unit
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 = parse(text)
160
- rendering = symbol ? embeddedmathml(asciimath2mathml(symbol)) :
161
- mathmlsymbol(units, false, multiplier)
162
- x.replace("#{delimspace(rendering, x)}<mrow xref='#{unit_id(origtext)}'>#{rendering}</mrow>\n"\
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 mn or mi, no space
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 = elem.xpath("./preceding-sibling::*[namespace-uri() = '#{MATHML_NS}']/"\
171
- "descendant::text()[normalize-space()!=''][last()]/parent::*").last
172
- return "" if prec_text_elem.nil? || !%w(mn mi).include?(prec_text_elem&.name)
173
- text = HTMLEntities.new.encode(Nokogiri::XML("<mrow>#{rendering}</mrow>").text.strip)
174
- /\p{L}|\p{N}/.match(text) ?
175
- "<mo rspace='thickmathspace'>&#x2062;</mo>" : "<mo>&#x2062;</mo>"
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'>&#x2062;</mo>"
156
+ else "<mo>&#x2062;</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 { |a| a.text }.uniq.each do |v|
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 == 0
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(:msword => true).append_expression(
192
- AsciiMath.parse(HTMLEntities.new.decode(expression)).ast).to_s.
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).each { |mi| mi["mathvariant"] = "normal" }
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 |(k, v), m|
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.keys.each { |k| u[k] = u[k].unshift(k) if @symbols.dig(k, :html) == k }
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 #{"| " * (maxcols - 1)}\n)
204
+ puts %([cols="#{maxcols + 1}*"]\n|===\n|Symbol | Unit + ID #{'| ' * (maxcols - 1)}\n)
219
205
  puts "\n\n"
220
- u.keys.sort_by { |a| [-u[a].size, a.gsub(%r{\&[^;]+;}, "").gsub(/[^A-Za-z]/, "").downcase] }.each do |k|
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 { |v1| v1.size }.each { |v1| print "| #{@units[v1].name}: `#{v1}` " }
223
- puts "#{"| " * (maxcols - u[k].size) }\n"
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(k)
229
- k.gsub(%r{<i>}, "__").gsub(%r{</i>}, "__")
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(x)
4
- case x
3
+ def multiplier(val)
4
+ case val
5
5
  when :space
6
6
  { html: "&#xA0;", mathml: "<mo rspace='thickmathspace'>&#x2062;</mo>" }
7
7
  when :nospace
8
8
  { html: "", mathml: "<mo>&#x2062;</mo>" }
9
9
  else
10
- { html: HTMLEntities.new.encode(x), mathml: "<mo>#{HTMLEntities.new.encode(x)}</mo>" }
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(x)
19
- HTMLEntities.new.decode(x).split(/([<>&])/)
20
- .map { |c| /[<>'"]/.match(c) ? c : HTMLEntities.new.encode(c, :hexadecimal) }.join
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] then u[:multiplier] == "*" ? @multiplier[:html] : 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] || "") + render(normalise ? @units[u[:unit]].symbolid : u[:unit], :html)
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(u, base)
36
- if u[:display_exponent] == "0.5"
39
+ def htmlsymbol_exponent(unit, base)
40
+ if unit[:display_exponent] == "0.5"
37
41
  base = "&#x221a;#{base}"
38
- elsif u[:display_exponent]
39
- exp = "<sup>#{u[:display_exponent].sub(/-/, "&#x2212;")}</sup>"
42
+ elsif unit[:display_exponent]
43
+ exp = "<sup>#{unit[:display_exponent].sub(/-/, '&#x2212;')}</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
- exp = units.map do |u|
48
- if u[:multiplier] then u[:multiplier] == "*" ? multiplier : "<mo>#{u[:multiplier]}</mo>"
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 mathmlsymbol1(u, normalise)
58
- base = render(normalise ? @units[u[:unit]].symbolid : u[:unit], :mathml)
59
- if u[:prefix]
60
- prefix = htmlent(@prefixes[u[:prefix]].html)
61
- base = base.match(/<mi mathvariant='normal'>/) ?
62
- base.sub(/<mi mathvariant='normal'>/, "<mi mathvariant='normal'>#{prefix}") :
63
- "<mrow><mi mathvariant='normal'>#{prefix}#{base}</mrow>"
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(u, base)
69
- if u[:display_exponent] == "0.5"
84
+ def mathmlsymbol_exponent(unit, base)
85
+ if unit[:display_exponent] == "0.5"
70
86
  base = "<msqrt>#{base}</msqrt>"
71
- elsif u[:display_exponent]
72
- exp = "<mn>#{u[:display_exponent]}</mn>".sub(/<mn>-/, "<mo>&#x2212;</mo><mn>")
87
+ elsif unit[:display_exponent]
88
+ exp = "<mn>#{unit[:display_exponent]}</mn>"
89
+ .sub(/<mn>-/, "<mo>&#x2212;</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
- <<~END
80
- <math xmlns='#{MATHML_NS}'><mrow>#{mathmlsymbol(units, normalise)}</mrow></math>
81
- END
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_" + (@units[text] ? @units[text].id.gsub(/'/, "_") : text.gsub(/\*/, ".").gsub(/\^/, ""))
10
+ "U_#{@units[text] ? @units[text].id.gsub(/'/, '_') : text.gsub(/\*/, '.').gsub(/\^/, '')}"
11
11
  end
12
12
 
13
- def unit(units, origtext, normtext, dims, name)
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
- <<~END
18
- <Unit xmlns='#{UNITSML_NS}' xml:id='#{unit_id(normtext)}'#{dimid ? " dimensionURL='##{dimid}'" : ""}>
19
- #{unitsystem(units)}
20
- #{unitname(norm_units, normtext, name)}
21
- #{unitsymbol(norm_units)}
22
- #{rootunits(units)}
23
- </Unit>
24
- END
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 && @units[units[0][:unit]].system_type == "SI-base"
45
- base = true if units.size == 1 && units[0][:unit] == "g" && units[0][:prefix] == "k"
46
- ret << "<UnitSystem name='SI' type='#{base ? "SI_base" : "SI_derived"}' xml:lang='en-US'/>"
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(units, text)
61
+ def compose_name(_units, text)
58
62
  text
59
63
  end
60
64
 
61
65
  def unitsymbol(units)
62
- <<~END
63
- <UnitSymbol type="HTML">#{htmlsymbol(units, true)}</UnitSymbol>
64
- <UnitSymbol type="MathML">#{mathmlsymbolwrap(units, true)}</UnitSymbol>
65
- END
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
- exponent = " powerNumerator='#{u[:exponent]}'" if u[:exponent] && u[:exponent] != "1"
74
- "<EnumeratedRootUnit unit='#{@units[u[:unit]].name}'#{prefix}#{exponent}/>"
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
- <<~END
77
- <RootUnits>#{exp}</RootUnits>
78
- END
82
+ <<~XML
83
+ <RootUnits>#{exp}</RootUnits>
84
+ XML
79
85
  end
80
86
  end
81
87
  end