asciimath2unitsml 0.3.0 → 0.4.0

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.
@@ -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
@@ -0,0 +1,59 @@
1
+ module Asciimath2UnitsML
2
+ class Conv
3
+ def validate_yaml(hash, path)
4
+ return hash if path == "../unitsdb/quantities.yaml"
5
+ return hash if path == "../unitsdb/dimensions.yaml"
6
+
7
+ hash.each_with_object({}) do |(k, v), m|
8
+ path == "../unitsdb/units.yaml" and validate_unit(v)
9
+ m = validate_symbols(m, v)
10
+ v[:unit_symbols]&.each { |s| validate_unit_symbol_cardinality(s, k) }
11
+ end
12
+ hash
13
+ end
14
+
15
+ def validate_unit(unit)
16
+ if unit[:quantity_reference]
17
+ unit[:quantity_reference].is_a?(Array) or
18
+ raise StandardError
19
+ .new "No quantity_reference array provided for unit: #{unit}"
20
+ end
21
+ if unit[:unit_name]
22
+ unit[:unit_name].is_a?(Array) or
23
+ raise StandardError
24
+ .new "No unit_name array provided for unit: #{unit}"
25
+ end
26
+ end
27
+
28
+ def validate_symbols(acc, val)
29
+ symbol = symbol_key(val)
30
+ !symbol.nil? or
31
+ raise StandardError.new "No symbol provided for unit: #{val}"
32
+ Array(symbol)&.each do |s|
33
+ acc[s] && s != "1" and
34
+ raise StandardError.new "symbol #{s} is not unique in #{val}: "\
35
+ "already used for #{acc[s]}"
36
+ acc[s] = val
37
+ end
38
+ acc
39
+ end
40
+
41
+ def validate_unit_symbol_cardinality(sym, key)
42
+ return true if sym.nil?
43
+
44
+ !sym[:id].nil? && !sym[:ascii].nil? && !sym[:html].nil? &&
45
+ !sym[:mathml].nil? && !sym[:latex].nil? &&
46
+ !sym[:unicode].nil? and return true
47
+ raise StandardError.new "malformed unit_symbol for #{key}: #{sym}"
48
+ end
49
+
50
+ def symbol_key(val)
51
+ symbol = val[:unit_symbols]&.each_with_object([]) do |s, m|
52
+ m << (s["id"] || s[:id])
53
+ end || val.dig(:symbol, :ascii) || val[:symbol] # || val[:short]
54
+ !symbol.nil? && val[:unit_symbols] && !symbol.is_a?(Array) and
55
+ symbol = [symbol]
56
+ symbol
57
+ end
58
+ end
59
+ end
@@ -1,3 +1,3 @@
1
1
  module Asciimath2UnitsML
2
- VERSION = '0.3.0'.freeze
2
+ VERSION = "0.4.0".freeze
3
3
  end
@@ -3,36 +3,86 @@ NISTd1:
3
3
  length:
4
4
  powerNumerator: 1
5
5
  symbol: L
6
+ dim_symbols:
7
+ - id: "dim_L"
8
+ ascii: "L"
9
+ html: "&#x1D5AB;"
10
+ mathml: "<mi mathvariant='sans-serif'>L</mi>"
11
+ latex: \ensuremath{\mathsf{L}}
12
+ unicode: "𝖫"
13
+
6
14
 
7
15
  NISTd2:
8
16
  mass:
9
17
  powerNumerator: 1
10
18
  symbol: M
19
+ dim_symbols:
20
+ - id: "dim_M"
21
+ ascii: "M"
22
+ html: "&#x1D5AC;"
23
+ mathml: "<mi mathvariant='sans-serif'>M</mi>"
24
+ latex: \ensuremath{\mathsf{M}}
25
+ unicode: "𝖬"
11
26
 
12
27
  NISTd3:
13
28
  time:
14
29
  powerNumerator: 1
15
30
  symbol: T
31
+ dim_symbols:
32
+ - id: "dim_T"
33
+ ascii: "T"
34
+ html: "&#x1D5B3;"
35
+ mathml: "<mi mathvariant='sans-serif'>T</mi>"
36
+ latex: \ensuremath{\mathsf{T}}
37
+ unicode: "𝖳"
16
38
 
17
39
  NISTd4:
18
40
  electric_current:
19
41
  powerNumerator: 1
20
42
  symbol: I
43
+ dim_symbols:
44
+ - id: "dim_T"
45
+ ascii: "T"
46
+ html: "&#x1D5A8;"
47
+ mathml: "<mi mathvariant='sans-serif'>T</mi>"
48
+ latex: \ensuremath{\mathsf{T}}
49
+ unicode: "𝖨"
21
50
 
22
51
  NISTd5:
23
52
  thermodynamic_temperature:
24
53
  powerNumerator: 1
25
- symbol:
54
+ symbol: Theta
55
+ dim_symbols:
56
+ - id: "dim_Theta"
57
+ ascii: "Theta"
58
+ html: "&#x1D760;"
59
+ mathml: "<mi mathvariant='sans-serif'>&#x398;</mi>"
60
+ latex: \ensuremath{\mathsf{\Theta}}
61
+ unicode: "𝝧"
26
62
 
27
63
  NISTd6:
28
64
  amount_of_substance:
29
65
  powerNumerator: 1
30
66
  symbol: N
67
+ dim_symbols:
68
+ - id: "dim_N"
69
+ ascii: "N"
70
+ html: "&#x1D5AD;"
71
+ mathml: "<mi mathvariant='sans-serif'>N</mi>"
72
+ latex: \ensuremath{\mathsf{N}}
73
+ unicode: "𝖭"
31
74
 
32
75
  NISTd7:
33
76
  luminous_intensity:
34
77
  powerNumerator: 1
35
78
  symbol: J
79
+ dim_symbols:
80
+ - id: "dim_J"
81
+ ascii: "J"
82
+ html: "&#x1D5A9;"
83
+ mathml: "<mi mathvariant='sans-serif'>J</mi>"
84
+ latex: \ensuremath{\mathsf{J}}
85
+ unicode: "𝖩"
36
86
 
37
87
  NISTd8:
38
88
  length:
@@ -44,6 +94,13 @@ NISTd9:
44
94
  plane_angle:
45
95
  powerNumerator: 1
46
96
  symbol: phi
97
+ dim_symbols:
98
+ - id: "dim_phi"
99
+ ascii: "phi"
100
+ html: "&#x1D785;"
101
+ mathml: "<mi mathvariant='sans-serif'>&#x3c6;</mi>"
102
+ latex: \ensuremath{\mathsf{\phi}}
103
+ unicode: "𝞅"
47
104
 
48
105
  NISTd10:
49
106
  length:
@@ -312,7 +369,7 @@ NISTd39:
312
369
  symbol: T
313
370
  thermodynamic_temperature:
314
371
  powerNumerator: -1
315
- symbol:
372
+ symbol: Theta
316
373
 
317
374
  NISTd36:
318
375
  length:
@@ -367,7 +424,7 @@ NISTd40:
367
424
  symbol: T
368
425
  thermodynamic_temperature:
369
426
  powerNumerator: -1
370
- symbol:
427
+ symbol: Theta
371
428
 
372
429
  NISTd41:
373
430
  length:
@@ -381,7 +438,7 @@ NISTd41:
381
438
  symbol: T
382
439
  thermodynamic_temperature:
383
440
  powerNumerator: -1
384
- symbol:
441
+ symbol: Theta
385
442
 
386
443
  NISTd46:
387
444
  length:
@@ -475,7 +532,7 @@ NISTd48:
475
532
  symbol: T
476
533
  thermodynamic_temperature:
477
534
  powerNumerator: -1
478
- symbol:
535
+ symbol: Theta
479
536
  amount_of_substance:
480
537
  powerNumerator: -1
481
538
  symbol: N
@@ -603,7 +660,7 @@ NISTd60:
603
660
  NISTd68:
604
661
  thermodynamic_temperature:
605
662
  powerNumerator: -1
606
- symbol:
663
+ symbol: Theta
607
664
 
608
665
  NISTd69:
609
666
  length:
@@ -617,7 +674,7 @@ NISTd69:
617
674
  symbol: T
618
675
  thermodynamic_temperature:
619
676
  powerNumerator: -1
620
- symbol:
677
+ symbol: Theta
621
678
 
622
679
  NISTd70:
623
680
  length:
@@ -639,7 +696,7 @@ NISTd71:
639
696
  symbol: T
640
697
  thermodynamic_temperature:
641
698
  powerNumerator: -1
642
- symbol:
699
+ symbol: Theta
643
700
 
644
701
  NISTd64:
645
702
  dimensionless: true