asciimath2unitsml 0.2.4 → 0.3.3

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: a2279898112af9b3602b0f7ed1a713adc4130015a3f616767d364952fb87b8ff
4
- data.tar.gz: 2bf900fe40159a836fed5bbbcdc7f76c7911083242e12962949b269fafe2e92d
3
+ metadata.gz: 829d573c90a88f5d26b2310d69f5ea575084df208cd3f441342435b0a6a3d353
4
+ data.tar.gz: 534c2ea7485b1de1c126de112a1ad8c9b10683797034e99313fda199c5f7e572
5
5
  SHA512:
6
- metadata.gz: 8d3c8c4ca3db1208f5764ddd0cf2ab221548dceb3ac99d69a14778bd05dc4aec07a54064ae408908aacf27f5dbdd456fbca6eb42f29cc626527d518f03ae831f
7
- data.tar.gz: 3b1d4a11f5db9225b20802ab261ba67099f977a66af680a1aaa19ffe2fdb6f6bbf78f0088f5473fe7b1e23083a72a32f9e130a9fe71abe240f9f306610bdef64
6
+ metadata.gz: c6eec2cb24fff7931d236a9a14a6d4ae1a938fe348d1e1d02a4d2f9be8e7f72e124e633ef0ef4b0f1e476ee8a2dee900210e60010600e0b082eb95237d2c70d2
7
+ data.tar.gz: e6943596be90a3bd1e6336b7bfa395cee4aa41e88ab6e5b2d003216a2271fc72249a018218d65c4ef446f4d246412e2b9fa0ac0d9721095abd4d9baa95515389
@@ -16,21 +16,11 @@ jobs:
16
16
  strategy:
17
17
  fail-fast: false
18
18
  matrix:
19
- ruby: [ '2.7', '2.6', '2.5', '2.4' ]
19
+ ruby: [ '3.0', '2.7', '2.6', '2.5', '2.4' ]
20
20
  os: [ ubuntu-latest, windows-latest, macos-latest ]
21
21
  experimental: [ false ]
22
- include:
23
- - ruby: '3.0'
24
- os: 'ubuntu-latest'
25
- experimental: true
26
- - ruby: '3.0'
27
- os: 'windows-latest'
28
- experimental: true
29
- - ruby: '3.0'
30
- os: 'macos-latest'
31
- experimental: true
32
22
  steps:
33
- - uses: actions/checkout@master
23
+ - uses: actions/checkout@v2
34
24
  with:
35
25
  submodules: true
36
26
 
@@ -40,14 +30,3 @@ jobs:
40
30
  bundler-cache: true
41
31
 
42
32
  - run: bundle exec rake
43
-
44
- tests-passed:
45
- needs: rake
46
- runs-on: ubuntu-latest
47
- steps:
48
- - uses: peter-evans/repository-dispatch@v1
49
- with:
50
- token: ${{ secrets.METANORMA_CI_PAT_TOKEN || secrets.GITHUB_TOKEN }}
51
- repository: ${{ github.repository }}
52
- event-type: notify
53
- client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}'
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .rubocop-https--*
data/.hound.yml ADDED
@@ -0,0 +1,5 @@
1
+ # Auto-generated by Cimas: Do not edit it manually!
2
+ # See https://github.com/metanorma/cimas
3
+ ruby:
4
+ enabled: true
5
+ config_file: .rubocop.yml
data/.rubocop.yml ADDED
@@ -0,0 +1,10 @@
1
+ # Auto-generated by Cimas: Do not edit it manually!
2
+ # See https://github.com/metanorma/cimas
3
+ inherit_from:
4
+ - https://raw.githubusercontent.com/riboseinc/oss-guides/master/ci/rubocop.yml
5
+
6
+ # local repo-specific modifications
7
+ # ...
8
+
9
+ AllCops:
10
+ TargetRubyVersion: 2.4
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source "https://rubygems.org"
2
-
2
+
3
3
  # Specify your gem's dependencies in ruby-vobject.gemspec
4
4
  gemspec
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
+
data/Rakefile CHANGED
@@ -3,4 +3,4 @@ require "rspec/core/rake_task"
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- task :default => :spec
6
+ task default: :spec
@@ -1,6 +1,6 @@
1
1
  # coding: utf-8
2
2
 
3
- lib = File.expand_path("../lib", __FILE__)
3
+ lib = File.expand_path("lib", __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
  require "asciimath2unitsml/version"
6
6
 
@@ -29,25 +29,23 @@ Gem::Specification.new do |spec|
29
29
  .split("\n")
30
30
  .map { |kv_str| kv_str.split(" ") }
31
31
  .each do |(_, submodule_path)|
32
+ # for each submodule, change working directory to that submodule
33
+ Dir.chdir(submodule_path) do
34
+ # issue git ls-files in submodule's directory
35
+ submodule_files = `git ls-files | grep -i '.yaml$'`.split($\)
32
36
 
33
- # for each submodule, change working directory to that submodule
34
- Dir.chdir(submodule_path) do
35
-
36
- # issue git ls-files in submodule's directory
37
- submodule_files = `git ls-files | grep -i '.yaml$'`.split($\)
38
-
39
- submodule_files_paths = submodule_files.map do |filename|
40
- File.join submodule_path, filename
41
- end
42
-
43
- # add relative paths to gem.files
44
- spec.files += submodule_files_paths
37
+ submodule_files_paths = submodule_files.map do |filename|
38
+ File.join submodule_path, filename
45
39
  end
40
+
41
+ # add relative paths to gem.files
42
+ spec.files += submodule_files_paths
43
+ end
46
44
  end
47
45
 
48
46
  spec.add_dependency "asciimath"
49
47
  spec.add_dependency "htmlentities"
50
- spec.add_dependency "nokogiri", "~> 1.10.4"
48
+ spec.add_dependency "nokogiri", "~> 1.11"
51
49
  spec.add_dependency "rsec", "~> 1.0.0"
52
50
 
53
51
  spec.add_development_dependency "bundler"
@@ -56,10 +54,9 @@ Gem::Specification.new do |spec|
56
54
  spec.add_development_dependency "guard", "~> 2.14"
57
55
  spec.add_development_dependency "guard-rspec", "~> 4.7"
58
56
  spec.add_development_dependency "rake", "~> 12.0"
57
+ spec.add_development_dependency "rexml"
59
58
  spec.add_development_dependency "rspec", "~> 3.6"
60
- spec.add_development_dependency "rubocop", "= 0.54.0"
59
+ spec.add_development_dependency "rubocop", "~> 1.5.2"
61
60
  spec.add_development_dependency "simplecov", "~> 0.15"
62
61
  spec.add_development_dependency "timecop", "~> 0.9"
63
- spec.add_development_dependency "rexml"
64
62
  end
65
-
data/bin/rspec CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
-
2
+
3
3
  # This file was generated by Bundler.
4
4
  #
5
5
  # The application 'rspec' is installed as part of a gem, and
@@ -15,4 +15,3 @@ require "rubygems"
15
15
  require "bundler/setup"
16
16
 
17
17
  load Gem.bin_path("rspec-core", "rspec")
18
-
@@ -1,4 +1,3 @@
1
1
  require_relative "asciimath2unitsml/version"
2
2
  require_relative "asciimath2unitsml/conv"
3
3
  require_relative "unitsdb_ruby/unitsdb"
4
-
@@ -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|
44
- <<~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>
53
- END
48
+ units.map { |u| u[:prefix] }.reject(&:nil?).uniq.map do |p|
49
+ <<~XML
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>
58
+ XML
54
59
  end.join("\n")
55
60
  end
56
61
 
57
62
  def dimension_components(dims)
58
63
  return if dims.nil? || dims.empty?
59
- <<~END
60
- <Dimension xmlns='#{UNITSML_NS}' xml:id="#{dim_id(dims)}">
61
- #{dims.map { |u| dimension1(u) }.join("\n") }
62
- </Dimension>
63
- END
64
+
65
+ <<~XML
66
+ <Dimension xmlns='#{UNITSML_NS}' xml:id="#{dim_id(dims)}">
67
+ #{dims.map { |u| dimension1(u) }.join("\n")}
68
+ </Dimension>
69
+ XML
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
- def dimension1(u)
91
- %(<#{u[:dimension]} symbol="#{u[:symbol]}" powerNumerator="#{float_to_display(u[:exponent])}"/>)
101
+ def dimension1(dim)
102
+ %(<#{dim[:dimension]} symbol="#{dim[:symbol]}"
103
+ powerNumerator="#{float_to_display(dim[: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,14 +182,17 @@ 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
162
- <<~END
163
- <Quantity xmlns='#{UNITSML_NS}' xml:id="#{id}"#{dim} quantityType="base">
164
- #{quantityname(id)}
165
- </Quantity>
166
- END
189
+ @units[normtext]&.dimension and
190
+ dim = %( dimensionURL="##{@units[normtext].dimension}")
191
+ <<~XML
192
+ <Quantity xmlns='#{UNITSML_NS}' xml:id="#{id}"#{dim} quantityType="base">
193
+ #{quantityname(id)}
194
+ </Quantity>
195
+ XML
167
196
  end
168
197
 
169
198
  def dimid2dimensions(normtext)
@@ -175,24 +204,25 @@ 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
- <<~END
181
- <Dimension xmlns='#{UNITSML_NS}' xml:id="#{@units[normtext]&.dimension}">
182
- #{dims.map { |u| dimension1(u) }.join("\n") }
183
- </Dimension>
184
- END
210
+ <<~XML
211
+ <Dimension xmlns='#{UNITSML_NS}' xml:id="#{@units[normtext]&.dimension}">
212
+ #{dims.map { |u| dimension1(u) }.join("\n")}
213
+ </Dimension>
214
+ XML
185
215
  end
186
216
 
187
217
  def unitsml(units, origtext, normtext, quantity, name)
188
218
  dims = units2dimensions(units)
189
- <<~END
190
- #{unit(units, origtext, normtext, dims, name)}
191
- #{prefix(units)}
192
- #{dimension(normtext)}
193
- #{dimension_components(dims)}
194
- #{quantity(normtext, quantity)}
195
- END
219
+ <<~XML
220
+ #{unit(units, origtext, normtext, dims, name)}
221
+ #{prefix(units)}
222
+ #{dimension(normtext)}
223
+ #{dimension_components(dims)}
224
+ #{quantity(normtext, quantity)}
225
+ XML
196
226
  end
197
227
  end
198
228
  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,46 +55,58 @@ 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
 
111
- def parse(x)
112
- text = Array(x.split(/,\s*/))
70
+ def parse(expr)
71
+ text = Array(expr.split(/,\s*/))
113
72
  units = @parser.parse!(text[0])
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)
123
- quantity = text[1..-1]&.select { |x| /^quantity:/.match(x) }&.first&.sub(/^quantity:\s*/, "")
124
- name = text[1..-1]&.select { |x| /^name:/.match(x) }&.first&.sub(/^name:\s*/, "")
125
- symbol = text[1..-1]&.select { |x| /^symbol:/.match(x) }&.first&.sub(/^symbol:\s*/, "")
82
+ units = postprocess1(units.flatten)
83
+ quantity = text[1..-1]&.select do |x|
84
+ /^quantity:/.match(x)
85
+ end&.first&.sub(/^quantity:\s*/, "")
86
+ name = text[1..-1]&.select do |x|
87
+ /^name:/.match(x)
88
+ end&.first&.sub(/^name:\s*/, "")
89
+ symbol = text[1..-1]&.select do |x|
90
+ /^symbol:/.match(x)
91
+ end&.first&.sub(/^symbol:\s*/, "")
92
+ multiplier = text[1..-1]&.select do |x|
93
+ /^multiplier:/.match(x)
94
+ end&.first&.sub(/^multiplier:\s*/, "")
126
95
  normtext = units_only(units).each.map do |u|
127
96
  exp = u[:exponent] && u[:exponent] != "1" ? "^#{u[:exponent]}" : ""
128
97
  "#{u[:prefix]}#{u[:unit]}#{exp}"
129
98
  end.join("*")
130
- [units, text[0], normtext, quantity, name, symbol]
99
+ [units, text[0], normtext, quantity, name, symbol, multiplier]
131
100
  end
132
101
 
133
102
  def postprocess1(units)
134
103
  inverse = false
135
- units.each_with_object([]) do |u, m|
104
+ units.each_with_object([]) do |u, m|
136
105
  if u[:multiplier]
137
- inverse = (u[:multiplier] == "/")
106
+ inverse = !inverse if u[:multiplier] == "/"
138
107
  else
139
- u[:exponent] = inverse ? "-#{u[:display_exponent] || '1'}" : u[:display_exponent]
108
+ u[:exponent] =
109
+ inverse ? "-#{u[:display_exponent] || '1'}" : u[:display_exponent]
140
110
  u[:exponent] = u[:exponent]&.sub(/^--+/, "")
141
111
  end
142
112
  m << u
@@ -148,32 +118,48 @@ module Asciimath2UnitsML
148
118
  MathML2UnitsML(xml).to_xml
149
119
  end
150
120
 
151
- # https://www.w3.org/TR/mathml-units/ section 2: delimit number Invisible-Times unit
121
+ # https://www.w3.org/TR/mathml-units/ section 2:
122
+ # delimit number Invisible-Times unit
152
123
  def MathML2UnitsML(xml)
153
124
  xml.is_a? String and xml = Nokogiri::XML(xml)
154
125
  xml.xpath(".//m:mtext", "m" => MATHML_NS).each do |x|
155
- next unless %r{^unitsml\(.+\)$}.match(x.text)
126
+ next unless %r{^unitsml\(.+\)$}.match?(x.text)
127
+
156
128
  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
- x.replace("#{delimspace(rendering, x)}<mrow xref='#{unit_id(origtext)}'>#{rendering}</mrow>\n"\
129
+ units, origtext, normtext, quantity, name, symbol, multiplier =
130
+ parse(text)
131
+ rendering = symbol ? embeddedmathml(asciimath2mathml(symbol)) :
132
+ mathmlsymbol(units, false, multiplier)
133
+ x.replace("#{delimspace(rendering, x)}"\
134
+ "<mrow xref='#{unit_id(origtext)}'>#{rendering}</mrow>\n"\
160
135
  "#{unitsml(units, origtext, normtext, quantity, name)}")
161
136
  end
162
137
  dedup_ids(xml)
163
138
  end
164
139
 
140
+ # if previous sibling's last descendent non-whitespace is MathML and
141
+ # mn or mi, no space
165
142
  def delimspace(rendering, elem)
166
- return "" if elem&.previous_element && elem&.previous_element.name != "mn"
167
- text = HTMLEntities.new.encode(Nokogiri::XML("<mrow>#{rendering}</mrow>").text.strip)
143
+ prec_text_elem =
144
+ elem.xpath("./preceding-sibling::*[namespace-uri() = '#{MATHML_NS}']/"\
145
+ "descendant::text()[normalize-space()!='']"\
146
+ "[last()]/parent::*").last
147
+ return "" if prec_text_elem.nil? ||
148
+ !%w(mn mi).include?(prec_text_elem&.name)
149
+
150
+ text = HTMLEntities.new.encode(Nokogiri::XML("<mrow>#{rendering}</mrow>")
151
+ .text.strip)
168
152
  /\p{L}|\p{N}/.match(text) ?
169
153
  "<mo rspace='thickmathspace'>&#x2062;</mo>" : "<mo>&#x2062;</mo>"
170
154
  end
171
155
 
172
156
  def dedup_ids(xml)
173
157
  %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|
158
+ xml.xpath(".//m:#{t}/@xml:id", "m" => UNITSML_NS).map(&:text)
159
+ .uniq.each do |v|
175
160
  xml.xpath(".//*[@xml:id = '#{v}']").each_with_index do |n, i|
176
- next if i == 0
161
+ next if i.zero?
162
+
177
163
  n.remove
178
164
  end
179
165
  end
@@ -183,44 +169,47 @@ module Asciimath2UnitsML
183
169
 
184
170
  def asciimath2mathml(expression)
185
171
  AsciiMath::MathMLBuilder.new(:msword => true).append_expression(
186
- AsciiMath.parse(HTMLEntities.new.decode(expression)).ast).to_s.
187
- gsub(/<math>/, "<math xmlns='#{MATHML_NS}'>")
172
+ AsciiMath.parse(HTMLEntities.new.decode(expression)).ast
173
+ ).to_s.gsub(/<math>/, "<math xmlns='#{MATHML_NS}'>")
188
174
  end
189
175
 
190
176
  def embeddedmathml(mathml)
191
177
  x = Nokogiri::XML(mathml)
192
- x.xpath(".//m:mi", "m" => MATHML_NS).each { |mi| mi["mathvariant"] = "normal" }
178
+ x.xpath(".//m:mi", "m" => MATHML_NS)
179
+ .each { |mi| mi["mathvariant"] = "normal" }
193
180
  x.children.to_xml
194
181
  end
195
182
 
196
183
  def ambig_units
197
- u = @units_id.each_with_object({}) do |(k, v), m|
184
+ u = @units_id.each_with_object({}) do |(_k, v), m|
198
185
  v.symbolids.each do |x|
199
186
  next if %r{[*/^]}.match(x)
200
187
  next unless v.symbols_hash[x][:html] != x
188
+
201
189
  m[v.symbols_hash[x][:html]] ||= []
202
190
  m[v.symbols_hash[x][:html]] << x
203
191
  end
204
192
  end
205
- u.keys.each { |k| u[k] = u[k].unshift(k) if @symbols.dig(k, :html) == k }
193
+ u.each_key { |k| u[k] = u[k].unshift(k) if @symbols.dig(k, :html) == k }
206
194
  render_ambig_units(u)
207
195
  end
208
196
 
209
197
  def render_ambig_units(u)
210
198
  maxcols = 0
211
199
  u.each { |_, v| maxcols = v.size if maxcols < v.size }
212
- puts %([cols="#{maxcols + 1}*"]\n|===\n|Symbol | Unit + ID #{"| " * (maxcols - 1)}\n)
200
+ puts %([cols="#{maxcols + 1}*"]\n|===\n|Symbol | Unit + ID #{'| ' * (maxcols - 1)}\n)
213
201
  puts "\n\n"
214
- u.keys.sort_by { |a| [-u[a].size, a.gsub(%r{\&[^;]+;}, "").gsub(/[^A-Za-z]/, "").downcase] }.each do |k|
202
+ u.keys.sort_by { |a| [-u[a].size, a.gsub(%r{\&[^;]+;}, "")
203
+ .gsub(/[^A-Za-z]/, "").downcase] }.each do |k|
215
204
  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"
205
+ u[k].sort_by(&:size).each { |v1| print "| #{@units[v1].name}: `#{v1}` " }
206
+ puts "#{'| ' * (maxcols - u[k].size)}\n"
218
207
  end
219
208
  puts "|===\n"
220
209
  end
221
210
 
222
- def html2adoc(k)
223
- k.gsub(%r{<i>}, "__").gsub(%r{</i>}, "__")
211
+ def html2adoc(elem)
212
+ elem.gsub(%r{<i>}, "__").gsub(%r{</i>}, "__")
224
213
  .gsub(%r{<sup>}, "^").gsub(%r{</sup>}, "^")
225
214
  .gsub(%r{<sub>}, "~").gsub(%r{</sub>}, "~")
226
215
  end