asciimath2unitsml 0.2.3 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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