asciimath2unitsml 0.2.3 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 161503157f4b3622c8bcae6ece418ed5f675a95f409d60017287017c923f7749
4
- data.tar.gz: 48571d4a30ea4ade2f864178dcb473c9a636d71d5088b64a2356e3cbfa5f2886
3
+ metadata.gz: f51b82e234fddaf66252f77a276a3acf68dae4356502a3c900253f5d4d4dffcf
4
+ data.tar.gz: 59dd18f009952329c1c07af2132ec06123c06dd3caea4ee7f22f17c0f4ec1cbc
5
5
  SHA512:
6
- metadata.gz: a50f75b036f2a19dcd1925965f192d1652ddabc242ce22c33efcfaf0d03c0ee5118c46021f22ee02237f93e5e32492c922466fd7f3206a20713c7a2bf749e6ef
7
- data.tar.gz: 279f88fb13d2ed6961b42fec192312b52b82d993daf4efa2750de21b2e240b2b63702d82dac831e4dc3a0b86bd6b797cbbca057809d4c971eb27bf71548f9010
6
+ metadata.gz: 9a925e3db85d3086d676c07c101609e87674d620394f5fa0a536ab3652f3aa9c0cc6a74507d79264e0154bf51ea4121d47b7275a94212bc934b822b0392a9970
7
+ data.tar.gz: 59d98ebd629a20e8f03dc32a1870474e5808b9d38f51a87feec5eae9f0746535bed9dcf38b1faf3939319bb3989b1d926088e8d834388e48f045070e3749312a
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .rubocop-https--*
data/.rubocop.yml ADDED
@@ -0,0 +1,14 @@
1
+ # This project follows the Ribose OSS style guide.
2
+ # https://github.com/riboseinc/oss-guides
3
+ # All project-specific additions and overrides should be specified in this file.
4
+ inherit_from:
5
+ - https://raw.githubusercontent.com/riboseinc/oss-guides/master/ci/rubocop.yml
6
+
7
+ # local repo-specific modifications
8
+
9
+ AllCops:
10
+ DisplayCopNames: false
11
+ StyleGuideCopsOnly: false
12
+ TargetRubyVersion: 2.4
13
+ Rails:
14
+ Enabled: true
data/README.adoc CHANGED
@@ -53,6 +53,8 @@ from UnitsDB. For example, `unitsml(cal_th/cm^2, name: langley)`.
53
53
  The unit-string gives the canonical representation of the unit, but SYMBOL is what will be rendered.
54
54
  For example, `unitsml(cal_th/cm^2, name: langley, symbol: La)`, or `unitsml(mm*s^-2, symbol: mm cdot s^-2)`.
55
55
  (All variables in SYMBOL are rendered upright, as is the default for units.)
56
+ * `unitsml(unit-string, multiplier: SYMBOL)` provides an alternate symbol for the multiliper of
57
+ units. The options are an XML entity, or the values `space` or `nospace` (for which see discussion under _Usage_).
56
58
 
57
59
  Standalone prefixes can be recognised by replacing the unit with hyphen; so `unitsml(p-)` corresponds
58
60
  to the standalone prefix "pico" (and is rendered as "p").
@@ -230,7 +232,7 @@ This table can be generated (in Asciidoc format) through `Asciimath2UnitsML::Con
230
232
  | ton | ton of TNT (energy equivalent): `ton_TNT` | ton of refrigeration (12 000 Btu_IT/h): `ton_refrigeration` | | | |
231
233
  | tsp | teaspoon: `tsp` | teaspoon (FDA): `tsp_label` | | | |
232
234
  | yd | yard: `yd` | yard (based on US survey foot): `yd_US_survey` | | | |
233
- | º | degree (degree of arc): `deg` | | | | |
235
+ | ° | degree (degree of arc): `deg` | | | | |
234
236
  | γ | gamma: `gamma` | | | | |
235
237
  | μ | micron: `micron` | | | | |
236
238
  | Ω | ohm: `Ohm` | | | | |
@@ -258,3 +260,4 @@ This table can be generated (in Asciidoc format) through `Asciimath2UnitsML::Con
258
260
  | °R | degree Rankine: `degR` | | | | |
259
261
  | ƛ~C~ | natural unit of length: `lambda-bar_C` | | | | |
260
262
  |===
263
+
@@ -57,7 +57,7 @@ Gem::Specification.new do |spec|
57
57
  spec.add_development_dependency "guard-rspec", "~> 4.7"
58
58
  spec.add_development_dependency "rake", "~> 12.0"
59
59
  spec.add_development_dependency "rspec", "~> 3.6"
60
- spec.add_development_dependency "rubocop", "= 0.54.0"
60
+ spec.add_development_dependency "rubocop", "~> 1.5.2"
61
61
  spec.add_development_dependency "simplecov", "~> 0.15"
62
62
  spec.add_development_dependency "timecop", "~> 0.9"
63
63
  spec.add_development_dependency "rexml"
@@ -7,70 +7,78 @@ require_relative "string"
7
7
  require_relative "parse"
8
8
  require_relative "render"
9
9
  require_relative "unit"
10
+ require_relative "validate"
10
11
 
11
12
  module Asciimath2UnitsML
12
13
  MATHML_NS = "http://www.w3.org/1998/Math/MathML".freeze
13
- UNITSML_NS = "http://unitsml.nist.gov/2005".freeze
14
+ UNITSML_NS = "https://schema.unitsml.org/unitsml/1.0".freeze
14
15
 
15
16
  class Conv
16
17
  def initialize(options = {})
17
- @dimensions_id = read_yaml("../unitsdb/dimensions.yaml").each_with_object({}) do |(k, v), m|
18
+ @dimensions_id = read_yaml("../unitsdb/dimensions.yaml")
19
+ .each_with_object({}) do |(k, v), m|
18
20
  m[k.to_s] = UnitsDB::Dimension.new(k, v)
19
21
  end
20
- @prefixes_id = read_yaml("../unitsdb/prefixes.yaml").each_with_object({}) do |(k, v), m|
22
+ @prefixes_id = read_yaml("../unitsdb/prefixes.yaml")
23
+ .each_with_object({}) do |(k, v), m|
21
24
  m[k] = UnitsDB::Prefix.new(k, v)
22
25
  end
23
26
  @prefixes = flip_name_and_symbol(@prefixes_id)
24
- @quantities = read_yaml("../unitsdb/quantities.yaml").each_with_object({}) do |(k, v), m|
27
+ @quantities = read_yaml("../unitsdb/quantities.yaml")
28
+ .each_with_object({}) do |(k, v), m|
25
29
  m[k.to_s] = UnitsDB::Quantity.new(k, v)
26
30
  end
27
- @units_id = read_yaml("../unitsdb/units.yaml").each_with_object({}) do |(k, v), m|
31
+ @units_id = read_yaml("../unitsdb/units.yaml")
32
+ .each_with_object({}) do |(k, v), m|
28
33
  m[k.to_s] = UnitsDB::Unit.new(k.to_s, v)
29
34
  end
30
35
  @units = flip_name_and_symbols(@units_id)
31
- @symbols = @units.each_with_object({}) do |(k, v), m|
36
+ @symbols = @units.each_with_object({}) do |(_k, v), m|
32
37
  v.symbolids.each { |x| m[x] = v.symbols_hash[x] }
33
38
  end
34
39
  @parser = parser
35
- @multiplier = multiplier(options[:multiplier] || "\u00b7")
40
+ @multiplier = multiplier(options[:multiplier] || "\u22c5")
36
41
  end
37
42
 
38
- def float_to_display(f)
39
- ret = f.to_f.round(1).to_s.sub(/\.0$/, "")
43
+ def float_to_display(float)
44
+ float.to_f.round(1).to_s.sub(/\.0$/, "")
40
45
  end
41
46
 
42
47
  def prefix(units)
43
- units.map { |u| u[:prefix] }.reject { |u| u.nil? }.uniq.map do |p|
48
+ units.map { |u| u[:prefix] }.reject(&:nil?).uniq.map do |p|
44
49
  <<~END
45
- <Prefix xmlns='#{UNITSML_NS}' prefixBase='#{@prefixes[p].base}'
46
- prefixPower='#{@prefixes[p].power}' xml:id='#{@prefixes[p].id}'>
47
- <PrefixName xml:lang="en">#{@prefixes[p].name}</PrefixName>
48
- <PrefixSymbol type="ASCII">#{@prefixes[p].ascii}</PrefixSymbol>
49
- <PrefixSymbol type="unicode">#{@prefixes[p].unicode}</PrefixSymbol>
50
- <PrefixSymbol type="LaTeX">#{@prefixes[p].latex}</PrefixSymbol>
51
- <PrefixSymbol type="HTML">#{htmlent @prefixes[p].html}</PrefixSymbol>
52
- </Prefix>
50
+ <Prefix xmlns='#{UNITSML_NS}' prefixBase='#{@prefixes[p].base}'
51
+ prefixPower='#{@prefixes[p].power}' xml:id='#{@prefixes[p].id}'>
52
+ <PrefixName xml:lang="en">#{@prefixes[p].name}</PrefixName>
53
+ <PrefixSymbol type="ASCII">#{@prefixes[p].ascii}</PrefixSymbol>
54
+ <PrefixSymbol type="unicode">#{@prefixes[p].unicode}</PrefixSymbol>
55
+ <PrefixSymbol type="LaTeX">#{@prefixes[p].latex}</PrefixSymbol>
56
+ <PrefixSymbol type="HTML">#{htmlent @prefixes[p].html}</PrefixSymbol>
57
+ </Prefix>
53
58
  END
54
59
  end.join("\n")
55
60
  end
56
61
 
57
62
  def dimension_components(dims)
58
63
  return if dims.nil? || dims.empty?
64
+
59
65
  <<~END
60
- <Dimension xmlns='#{UNITSML_NS}' xml:id="#{dim_id(dims)}">
61
- #{dims.map { |u| dimension1(u) }.join("\n") }
62
- </Dimension>
66
+ <Dimension xmlns='#{UNITSML_NS}' xml:id="#{dim_id(dims)}">
67
+ #{dims.map { |u| dimension1(u) }.join("\n")}
68
+ </Dimension>
63
69
  END
64
70
  end
65
71
 
66
- U2D = {
72
+ U2D = {
67
73
  "m" => { dimension: "Length", order: 1, symbol: "L" },
68
74
  "g" => { dimension: "Mass", order: 2, symbol: "M" },
69
75
  "kg" => { dimension: "Mass", order: 2, symbol: "M" },
70
76
  "s" => { dimension: "Time", order: 3, symbol: "T" },
71
77
  "A" => { dimension: "ElectricCurrent", order: 4, symbol: "I" },
72
- "K" => { dimension: "ThermodynamicTemperature", order: 5, symbol: "Theta" },
73
- "degK" => { dimension: "ThermodynamicTemperature", order: 5, symbol: "Theta" },
78
+ "K" => { dimension: "ThermodynamicTemperature", order: 5,
79
+ symbol: "Theta" },
80
+ "degK" => { dimension: "ThermodynamicTemperature", order: 5,
81
+ symbol: "Theta" },
74
82
  "mol" => { dimension: "AmountOfSubstance", order: 6, symbol: "N" },
75
83
  "cd" => { dimension: "LuminousIntensity", order: 7, symbol: "J" },
76
84
  "deg" => { dimension: "PlaneAngle", order: 8, symbol: "Phi" },
@@ -78,28 +86,35 @@ module Asciimath2UnitsML
78
86
 
79
87
  def units2dimensions(units)
80
88
  norm = decompose_units(units)
81
- return if norm.any? { |u| u[:unit] == "unknown" || u[:prefix] == "unknown" || u[:unit].nil? }
89
+ return if norm.any? do |u|
90
+ u[:unit] == "unknown" || u[:prefix] == "unknown" || u[:unit].nil?
91
+ end
92
+
82
93
  norm.map do |u|
83
94
  { dimension: U2D[u[:unit]][:dimension],
84
95
  unit: u[:unit],
85
96
  exponent: u[:exponent] || 1,
86
- symbol: U2D[u[:unit]][:symbol] }
97
+ symbol: U2D[u[:unit]][:symbol] }
87
98
  end.sort { |a, b| U2D[a[:unit]][:order] <=> U2D[b[:unit]][:order] }
88
99
  end
89
100
 
90
101
  def dimension1(u)
91
- %(<#{u[:dimension]} symbol="#{u[:symbol]}" powerNumerator="#{float_to_display(u[:exponent])}"/>)
102
+ %(<#{u[:dimension]} symbol="#{u[:symbol]}"
103
+ powerNumerator="#{float_to_display(u[:exponent])}"/>)
92
104
  end
93
105
 
94
106
  def dim_id(dims)
95
107
  return nil if dims.nil? || dims.empty?
108
+
96
109
  dimhash = dims.each_with_object({}) { |h, m| m[h[:dimension]] = h }
97
- dimsvector = %w(Length Mass Time ElectricCurrent ThermodynamicTemperature
110
+ dimsvector = %w(Length Mass Time ElectricCurrent ThermodynamicTemperature
98
111
  AmountOfSubstance LuminousIntensity PlaneAngle)
99
112
  .map { |h| dimhash.dig(h, :exponent) }.join(":")
100
- id = @dimensions_id&.values&.select { |d| d.vector == dimsvector }&.first&.id and return id.to_s
113
+ id = @dimensions_id&.values&.select { |d| d.vector == dimsvector }
114
+ &.first&.id and return id.to_s
101
115
  "D_" + dims.map do |d|
102
- U2D[d[:unit]][:symbol] + (d[:exponent] == 1 ? "" : float_to_display(d[:exponent]))
116
+ U2D[d[:unit]][:symbol] +
117
+ (d[:exponent] == 1 ? "" : float_to_display(d[:exponent]))
103
118
  end.join("")
104
119
  end
105
120
 
@@ -108,12 +123,17 @@ module Asciimath2UnitsML
108
123
  end
109
124
 
110
125
  def gather_units(units)
111
- units.sort { |a, b| a[:unit] <=> b[:unit] }.each_with_object([]) do |k, m|
126
+ units.sort_by { |a| a[:unit] }.each_with_object([]) do |k, m|
112
127
  if m.empty? || m[-1][:unit] != k[:unit] then m << k
113
128
  else
114
- m[-1] = { prefix: combine_prefixes(@prefixes[m[-1][:prefix]], @prefixes[k[:prefix]]),
115
- unit: m[-1][:unit],
116
- exponent: (k[:exponent]&.to_f || 1) + (m[-1][:exponent]&.to_f || 1) }
129
+ m[-1] = {
130
+ prefix: combine_prefixes(
131
+ @prefixes[m[-1][:prefix]], @prefixes[k[:prefix]]
132
+ ),
133
+ unit: m[-1][:unit],
134
+ exponent: (k[:exponent]&.to_f || 1) +
135
+ (m[-1][:exponent]&.to_f || 1),
136
+ }
117
137
  end
118
138
  end
119
139
  end
@@ -128,8 +148,13 @@ module Asciimath2UnitsML
128
148
  { prefix: u[:prefix], unit: "unknown", exponent: u[:exponent] }
129
149
  else
130
150
  @units[u[:unit]].si_derived_bases.each_with_object([]) do |k, m|
131
- m << { prefix: !k[:prefix].nil? && !k[:prefix].empty? ?
132
- combine_prefixes(@prefixes_id[k[:prefix]], @prefixes[u[:prefix]]) : u[:prefix],
151
+ prefix = if !k[:prefix].nil? && !k[:prefix].empty?
152
+ combine_prefixes(@prefixes_id[k[:prefix]],
153
+ @prefixes[u[:prefix]])
154
+ else
155
+ u[:prefix]
156
+ end
157
+ m << { prefix: prefix,
133
158
  unit: @units_id[k[:id]].symbolid,
134
159
  exponent: (k[:power]&.to_i || 1) * (u[:exponent]&.to_f || 1) }
135
160
  end
@@ -141,6 +166,7 @@ module Asciimath2UnitsML
141
166
  return p1.symbolid if p2.nil?
142
167
  return p2.symbolid if p1.nil?
143
168
  return "unknown" if p1.base != p2.base
169
+
144
170
  @prefixes.each do |_, p|
145
171
  return p.symbolid if p.base == p1.base && p.power == p1.power + p2.power
146
172
  end
@@ -156,13 +182,16 @@ module Asciimath2UnitsML
156
182
  end
157
183
 
158
184
  def quantity(normtext, quantity)
159
- return unless @units[normtext] && @units[normtext].quantities.size == 1 || @quantities[quantity]
185
+ return unless @units[normtext] && @units[normtext].quantities.size == 1 ||
186
+ @quantities[quantity]
187
+
160
188
  id = quantity || @units[normtext].quantities.first
161
- dim = %( dimensionURL="##{@units[normtext].dimension}") if @units[normtext]&.dimension
189
+ @units[normtext]&.dimension and
190
+ dim = %( dimensionURL="##{@units[normtext].dimension}")
162
191
  <<~END
163
- <Quantity xmlns='#{UNITSML_NS}' xml:id="#{id}"#{dim} quantityType="base">
164
- #{quantityname(id)}
165
- </Quantity>
192
+ <Quantity xmlns='#{UNITSML_NS}' xml:id="#{id}"#{dim} quantityType="base">
193
+ #{quantityname(id)}
194
+ </Quantity>
166
195
  END
167
196
  end
168
197
 
@@ -175,23 +204,24 @@ module Asciimath2UnitsML
175
204
  end
176
205
 
177
206
  def dimension(normtext)
178
- return unless @units[normtext]&.dimension
207
+ return unless @units[normtext]&.dimension
208
+
179
209
  dims = dimid2dimensions(@units[normtext]&.dimension)
180
210
  <<~END
181
- <Dimension xmlns='#{UNITSML_NS}' xml:id="#{@units[normtext]&.dimension}">
182
- #{dims.map { |u| dimension1(u) }.join("\n") }
183
- </Dimension>
211
+ <Dimension xmlns='#{UNITSML_NS}' xml:id="#{@units[normtext]&.dimension}">
212
+ #{dims.map { |u| dimension1(u) }.join("\n")}
213
+ </Dimension>
184
214
  END
185
215
  end
186
216
 
187
217
  def unitsml(units, origtext, normtext, quantity, name)
188
218
  dims = units2dimensions(units)
189
219
  <<~END
190
- #{unit(units, origtext, normtext, dims, name)}
191
- #{prefix(units)}
192
- #{dimension(normtext)}
193
- #{dimension_components(dims)}
194
- #{quantity(normtext, quantity)}
220
+ #{unit(units, origtext, normtext, dims, name)}
221
+ #{prefix(units)}
222
+ #{dimension(normtext)}
223
+ #{dimension_components(dims)}
224
+ #{quantity(normtext, quantity)}
195
225
  END
196
226
  end
197
227
  end
@@ -3,71 +3,29 @@ module Asciimath2UnitsML
3
3
  include Rsec::Helpers
4
4
 
5
5
  def read_yaml(path)
6
- validate_yaml(symbolize_keys(YAML.load_file(File.join(File.join(File.dirname(__FILE__), path)))), path)
6
+ validate_yaml(symbolize_keys(YAML
7
+ .load_file(File.join(File.join(File.dirname(__FILE__), path)))), path)
7
8
  end
8
9
 
9
10
  def flip_name_and_symbol(hash)
10
- hash.each_with_object({}) do |(k, v), m|
11
+ hash.each_with_object({}) do |(_k, v), m|
11
12
  next if v.name.nil? || v.name.empty?
13
+
12
14
  m[v.symbolid] = v
13
15
  end
14
16
  end
15
17
 
16
18
  def flip_name_and_symbols(hash)
17
- hash.each_with_object({}) do |(k, v), m|
19
+ hash.each_with_object({}) do |(_k, v), m|
18
20
  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
42
- end
43
21
 
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
22
+ v.symbolids.each { |s| m[s] = v }
51
23
  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
24
  end
68
25
 
69
26
  def symbolize_keys(hash)
70
27
  return hash if hash.is_a? String
28
+
71
29
  hash.inject({}) do |result, (key, value)|
72
30
  new_key = case key
73
31
  when String then key.to_sym
@@ -97,14 +55,15 @@ module Asciimath2UnitsML
97
55
  seq("sqrt(", unit1, ")") { |x| { prefix: nil, unit: x[1], display_exponent: "0.5" } } |
98
56
  seq("sqrt(", prefix1, unit1, ")") { |x| { prefix: x[1], unit: x[2], display_exponent: "0.5" } } |
99
57
  seq("sqrt(", prefix2, unit1, ")") { |x| { prefix: x[1], unit: x[2], display_exponent: "0.5" } } |
100
- seq(unit1, exponent._? & multiplier) { |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] ) } } |
104
- "1".r.map { |_| { prefix: nil, unit: "1", display_exponent: nil } }
58
+ seq(unit1, exponent._? & (multiplier | ")".r)) { |x| { prefix: nil, unit: x[0], display_exponent: (x[1][0]) } } |
59
+ seq(unit1, exponent._?).eof { |x| { prefix: nil, unit: x[0], display_exponent: (x[1][0]) } } |
60
+ seq(prefix1, unit1, exponent._? ) { |x| { prefix: x[0], unit: x[1], display_exponent: (x[2][0]) } } |
61
+ seq(prefix2, unit1, exponent._? ) { |x| { prefix: x[0], unit: x[1], display_exponent: (x[2][0]) } } |
62
+ "1".r.map { |_| { prefix: nil, unit: "1", display_exponent: nil } }
63
+ units1 = "(".r >> lazy{units} << ")" | unit
105
64
  units = seq(prefix2, "-") { |x| [{ prefix: x[0], unit: nil, display_exponent: nil }] } |
106
65
  seq(prefix1, "-") { |x| [{ prefix: x[0], unit: nil, display_exponent: nil }] } |
107
- unit.join(multiplier)
66
+ units1.join(multiplier)
108
67
  parser = units.eof
109
68
  end
110
69
 
@@ -114,29 +73,32 @@ module Asciimath2UnitsML
114
73
  if !units || Rsec::INVALID[units]
115
74
  raise Rsec::SyntaxError.new "error parsing UnitsML expression", x, 1, 0
116
75
  end
76
+
117
77
  Rsec::Fail.reset
118
78
  postprocess(units, text)
119
79
  end
120
80
 
121
81
  def postprocess(units, text)
122
- units = postprocess1(units)
82
+ units = postprocess1(units.flatten)
123
83
  quantity = text[1..-1]&.select { |x| /^quantity:/.match(x) }&.first&.sub(/^quantity:\s*/, "")
124
84
  name = text[1..-1]&.select { |x| /^name:/.match(x) }&.first&.sub(/^name:\s*/, "")
125
85
  symbol = text[1..-1]&.select { |x| /^symbol:/.match(x) }&.first&.sub(/^symbol:\s*/, "")
86
+ multiplier = text[1..-1]&.select { |x| /^multiplier:/.match(x) }&.first&.sub(/^multiplier:\s*/, "")
126
87
  normtext = units_only(units).each.map do |u|
127
88
  exp = u[:exponent] && u[:exponent] != "1" ? "^#{u[:exponent]}" : ""
128
89
  "#{u[:prefix]}#{u[:unit]}#{exp}"
129
90
  end.join("*")
130
- [units, text[0], normtext, quantity, name, symbol]
91
+ [units, text[0], normtext, quantity, name, symbol, multiplier]
131
92
  end
132
93
 
133
94
  def postprocess1(units)
134
95
  inverse = false
135
- units.each_with_object([]) do |u, m|
96
+ units.each_with_object([]) do |u, m|
136
97
  if u[:multiplier]
137
- inverse = (u[:multiplier] == "/")
98
+ inverse = !inverse if u[:multiplier] == "/"
138
99
  else
139
- u[:exponent] = inverse ? "-#{u[:display_exponent] || '1'}" : u[:display_exponent]
100
+ u[:exponent] =
101
+ inverse ? "-#{u[:display_exponent] || '1'}" : u[:display_exponent]
140
102
  u[:exponent] = u[:exponent]&.sub(/^--+/, "")
141
103
  end
142
104
  m << u
@@ -153,27 +115,39 @@ module Asciimath2UnitsML
153
115
  xml.is_a? String and xml = Nokogiri::XML(xml)
154
116
  xml.xpath(".//m:mtext", "m" => MATHML_NS).each do |x|
155
117
  next unless %r{^unitsml\(.+\)$}.match(x.text)
118
+
156
119
  text = x.text.sub(%r{^unitsml\((.+)\)$}m, "\\1")
157
- units, origtext, normtext, quantity, name, symbol = parse(text)
158
- rendering = symbol ? embeddedmathml(asciimath2mathml(symbol)) : mathmlsymbol(units, false)
159
- delim = x&.previous_element ? delimspace(rendering) : ""
160
- x.replace("#{delim}<mrow xref='#{unit_id(origtext)}'>#{rendering}</mrow>\n"\
120
+ units, origtext, normtext, quantity, name, symbol, multiplier = parse(text)
121
+ rendering = symbol ? embeddedmathml(asciimath2mathml(symbol)) :
122
+ mathmlsymbol(units, false, multiplier)
123
+ x.replace("#{delimspace(rendering, x)}"\
124
+ "<mrow xref='#{unit_id(origtext)}'>#{rendering}</mrow>\n"\
161
125
  "#{unitsml(units, origtext, normtext, quantity, name)}")
162
126
  end
163
127
  dedup_ids(xml)
164
128
  end
165
129
 
166
- def delimspace(x)
167
- text = HTMLEntities.new.encode(Nokogiri::XML("<mrow>#{x}</mrow>").text.strip)
168
- /[[:alnum:]]/.match(text) ?
130
+ # if previous sibling's last descendent non-whitespace is MathML and mn or mi, no space
131
+ def delimspace(rendering, elem)
132
+ prec_text_elem =
133
+ elem.xpath("./preceding-sibling::*[namespace-uri() = '#{MATHML_NS}']/"\
134
+ "descendant::text()[normalize-space()!=''][last()]/parent::*").last
135
+ return "" if prec_text_elem.nil? ||
136
+ !%w(mn mi).include?(prec_text_elem&.name)
137
+
138
+ text = HTMLEntities.new.encode(Nokogiri::XML("<mrow>#{rendering}</mrow>")
139
+ .text.strip)
140
+ /\p{L}|\p{N}/.match(text) ?
169
141
  "<mo rspace='thickmathspace'>&#x2062;</mo>" : "<mo>&#x2062;</mo>"
170
142
  end
171
143
 
172
144
  def dedup_ids(xml)
173
145
  %w(Unit Dimension Prefix Quantity).each do |t|
174
- xml.xpath(".//m:#{t}/@xml:id", "m" => UNITSML_NS).map { |a| a.text }.uniq.each do |v|
146
+ xml.xpath(".//m:#{t}/@xml:id", "m" => UNITSML_NS).map(&:text)
147
+ .uniq.each do |v|
175
148
  xml.xpath(".//*[@xml:id = '#{v}']").each_with_index do |n, i|
176
- next if i == 0
149
+ next if i.zero?
150
+
177
151
  n.remove
178
152
  end
179
153
  end
@@ -183,26 +157,28 @@ module Asciimath2UnitsML
183
157
 
184
158
  def asciimath2mathml(expression)
185
159
  AsciiMath::MathMLBuilder.new(:msword => true).append_expression(
186
- AsciiMath.parse(HTMLEntities.new.decode(expression)).ast).to_s.
187
- gsub(/<math>/, "<math xmlns='#{MATHML_NS}'>")
160
+ AsciiMath.parse(HTMLEntities.new.decode(expression)).ast).to_s
161
+ .gsub(/<math>/, "<math xmlns='#{MATHML_NS}'>")
188
162
  end
189
163
 
190
164
  def embeddedmathml(mathml)
191
165
  x = Nokogiri::XML(mathml)
192
- x.xpath(".//m:mi", "m" => MATHML_NS).each { |mi| mi["mathvariant"] = "normal" }
166
+ x.xpath(".//m:mi", "m" => MATHML_NS)
167
+ .each { |mi| mi["mathvariant"] = "normal" }
193
168
  x.children.to_xml
194
169
  end
195
170
 
196
171
  def ambig_units
197
- u = @units_id.each_with_object({}) do |(k, v), m|
172
+ u = @units_id.each_with_object({}) do |(_k, v), m|
198
173
  v.symbolids.each do |x|
199
174
  next if %r{[*/^]}.match(x)
200
175
  next unless v.symbols_hash[x][:html] != x
176
+
201
177
  m[v.symbols_hash[x][:html]] ||= []
202
178
  m[v.symbols_hash[x][:html]] << x
203
179
  end
204
180
  end
205
- u.keys.each { |k| u[k] = u[k].unshift(k) if @symbols.dig(k, :html) == k }
181
+ u.each_key { |k| u[k] = u[k].unshift(k) if @symbols.dig(k, :html) == k }
206
182
  render_ambig_units(u)
207
183
  end
208
184
 
@@ -211,16 +187,17 @@ module Asciimath2UnitsML
211
187
  u.each { |_, v| maxcols = v.size if maxcols < v.size }
212
188
  puts %([cols="#{maxcols + 1}*"]\n|===\n|Symbol | Unit + ID #{"| " * (maxcols - 1)}\n)
213
189
  puts "\n\n"
214
- u.keys.sort_by { |a| [-u[a].size, a.gsub(%r{\&[^;]+;}, "").gsub(/[^A-Za-z]/, "").downcase] }.each do |k|
190
+ u.keys.sort_by { |a| [-u[a].size, a.gsub(%r{\&[^;]+;}, "")
191
+ .gsub(/[^A-Za-z]/, "").downcase] }.each do |k|
215
192
  print "| #{html2adoc(k)} "
216
- u[k].sort_by { |v1| v1.size }.each { |v1| print "| #{@units[v1].name}: `#{v1}` " }
217
- puts "#{"| " * (maxcols - u[k].size) }\n"
193
+ u[k].sort_by(&:size).each { |v1| print "| #{@units[v1].name}: `#{v1}` " }
194
+ puts "#{'| ' * (maxcols - u[k].size) }\n"
218
195
  end
219
196
  puts "|===\n"
220
197
  end
221
198
 
222
- def html2adoc(k)
223
- k.gsub(%r{<i>}, "__").gsub(%r{</i>}, "__")
199
+ def html2adoc(elem)
200
+ elem.gsub(%r{<i>}, "__").gsub(%r{</i>}, "__")
224
201
  .gsub(%r{<sup>}, "^").gsub(%r{</sup>}, "^")
225
202
  .gsub(%r{<sub>}, "~").gsub(%r{</sub>}, "~")
226
203
  end