asciimath2unitsml 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/rake.yml +1 -1
- data/.rubocop.yml +1 -1
- data/README.adoc +19 -1
- data/asciimath2unitsml.gemspec +1 -1
- data/lib/asciimath2unitsml/conv.rb +26 -83
- data/lib/asciimath2unitsml/dimensions.rb +117 -0
- data/lib/asciimath2unitsml/parse.rb +79 -73
- data/lib/asciimath2unitsml/read.rb +42 -0
- data/lib/asciimath2unitsml/render.rb +29 -18
- data/lib/asciimath2unitsml/unit.rb +10 -8
- data/lib/asciimath2unitsml/validate.rb +18 -16
- data/lib/asciimath2unitsml/version.rb +1 -1
- data/lib/unitsdb/dimensions.yaml +65 -8
- data/lib/unitsdb_ruby/unitsdb.rb +83 -61
- data/spec/conv_spec.rb +1325 -1249
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b4d619489e5063d9e3616163d69c6c1f4796615412ee40f0a92f356603f0e366
|
4
|
+
data.tar.gz: 0465ac8a99f8929d1d4f8f2db152821e4d1fae7ac66af1a8a3e2feb6092e0c3a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3de256a40d7eb24439409664a8b7bb14f1893403a4cfc762b70bd9693a15c0304766a7d20491de4094571fd5b117695350a5ad56100cf67bde1e7c61c4e99004
|
7
|
+
data.tar.gz: 4f053e85ff192f040856d0f63ff2d1ffb7a49eb72735f668785a3396e82ff145b8d678d754b8a26b26deee4c3072de4f8af8686f7ee60cb9e339dfe3f7be08c3
|
data/.github/workflows/rake.yml
CHANGED
data/.rubocop.yml
CHANGED
data/README.adoc
CHANGED
@@ -59,6 +59,24 @@ units. The options are an XML entity, or the values `space` or `nospace` (for wh
|
|
59
59
|
Standalone prefixes can be recognised by replacing the unit with hyphen; so `unitsml(p-)` corresponds
|
60
60
|
to the standalone prefix "pico" (and is rendered as "p").
|
61
61
|
|
62
|
+
The gem also supports fundamental units, e.g. `unitsml(e)` for the atomic unit of charge, _e_,
|
63
|
+
and symbols for dimensions. The latter are entered as `dim_XXX`, where `XXX` is their established symbol:
|
64
|
+
|
65
|
+
|===
|
66
|
+
|Symbol | Dimension
|
67
|
+
|
68
|
+
|dim_L | Length
|
69
|
+
|dim_M | Mass
|
70
|
+
|dim_T | Time
|
71
|
+
|dim_I | Electric Current
|
72
|
+
|dim_Theta | Thermodynamic Temperature
|
73
|
+
|dim_N | Amount of Substance
|
74
|
+
|dim_J | Luminous Intensity
|
75
|
+
|dim_phi | Plane Angle (dimensionless)
|
76
|
+
|===
|
77
|
+
|
78
|
+
e.g. `unitsml(dim_I)` for the dimension of electric current, 𝖨.
|
79
|
+
|
62
80
|
== Rendering
|
63
81
|
|
64
82
|
The output of the gem is MathML, with MathML unit expressions (expressed as `<mi>`,
|
@@ -141,7 +159,7 @@ The converter is run as:
|
|
141
159
|
[source,ruby]
|
142
160
|
----
|
143
161
|
c = Asciimath2UnitsML::Conv.new()
|
144
|
-
c.Asciimath2UnitsML(1 "unitsml(mm*s^-2)") # AsciiMath string containing UnitsML
|
162
|
+
c.Asciimath2UnitsML('1 "unitsml(mm*s^-2)"') # AsciiMath string containing UnitsML
|
145
163
|
c.MathML2UnitsML("<math xmlns='http://www.w3.org/1998/Math/MathML'><mn>7</mn>"\
|
146
164
|
"<mtext>unitsml(kg^-2)</mtext></math>") # AsciiMath string containing <mtext>unitsml()</mtext>
|
147
165
|
c.MathML2UnitsML(Nokogiri::XML("<math xmlns='http://www.w3.org/1998/Math/MathML'><mn>7</mn>"\
|
data/asciimath2unitsml.gemspec
CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
spec.files = `git ls-files`.split("\n")
|
24
24
|
spec.test_files = `git ls-files -- {spec}/*`.split("\n")
|
25
|
-
spec.required_ruby_version = Gem::Requirement.new(">= 2.
|
25
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
|
26
26
|
|
27
27
|
# get an array of submodule dirs relatively to root repo
|
28
28
|
`git config --file .gitmodules --get-regexp '\\.path$'`
|
@@ -3,6 +3,8 @@ require "nokogiri"
|
|
3
3
|
require "htmlentities"
|
4
4
|
require "yaml"
|
5
5
|
require "rsec"
|
6
|
+
require_relative "read"
|
7
|
+
require_relative "dimensions"
|
6
8
|
require_relative "string"
|
7
9
|
require_relative "parse"
|
8
10
|
require_relative "render"
|
@@ -19,6 +21,7 @@ module Asciimath2UnitsML
|
|
19
21
|
.each_with_object({}) do |(k, v), m|
|
20
22
|
m[k.to_s] = UnitsDB::Dimension.new(k, v)
|
21
23
|
end
|
24
|
+
@dimensions = flip_name_and_symbols(@dimensions_id)
|
22
25
|
@prefixes_id = read_yaml("../unitsdb/prefixes.yaml")
|
23
26
|
.each_with_object({}) do |(k, v), m|
|
24
27
|
m[k] = UnitsDB::Prefix.new(k, v)
|
@@ -33,10 +36,10 @@ module Asciimath2UnitsML
|
|
33
36
|
m[k.to_s] = UnitsDB::Unit.new(k.to_s, v)
|
34
37
|
end
|
35
38
|
@units = flip_name_and_symbols(@units_id)
|
36
|
-
@symbols = @units.each_with_object({}) do |(_k, v), m|
|
39
|
+
@symbols = @units.merge(@dimensions).each_with_object({}) do |(_k, v), m|
|
37
40
|
v.symbolids.each { |x| m[x] = v.symbols_hash[x] }
|
38
41
|
end
|
39
|
-
@parser =
|
42
|
+
@parser, @dim_parser = parsers
|
40
43
|
@multiplier = multiplier(options[:multiplier] || "\u22c5")
|
41
44
|
end
|
42
45
|
|
@@ -59,70 +62,17 @@ module Asciimath2UnitsML
|
|
59
62
|
end.join("\n")
|
60
63
|
end
|
61
64
|
|
62
|
-
def dimension_components(dims)
|
63
|
-
return if dims.nil? || dims.empty?
|
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
|
70
|
-
end
|
71
|
-
|
72
|
-
U2D = {
|
73
|
-
"m" => { dimension: "Length", order: 1, symbol: "L" },
|
74
|
-
"g" => { dimension: "Mass", order: 2, symbol: "M" },
|
75
|
-
"kg" => { dimension: "Mass", order: 2, symbol: "M" },
|
76
|
-
"s" => { dimension: "Time", order: 3, symbol: "T" },
|
77
|
-
"A" => { dimension: "ElectricCurrent", order: 4, symbol: "I" },
|
78
|
-
"K" => { dimension: "ThermodynamicTemperature", order: 5,
|
79
|
-
symbol: "Theta" },
|
80
|
-
"degK" => { dimension: "ThermodynamicTemperature", order: 5,
|
81
|
-
symbol: "Theta" },
|
82
|
-
"mol" => { dimension: "AmountOfSubstance", order: 6, symbol: "N" },
|
83
|
-
"cd" => { dimension: "LuminousIntensity", order: 7, symbol: "J" },
|
84
|
-
"deg" => { dimension: "PlaneAngle", order: 8, symbol: "Phi" },
|
85
|
-
}.freeze
|
86
|
-
|
87
|
-
def units2dimensions(units)
|
88
|
-
norm = decompose_units(units)
|
89
|
-
return if norm.any? do |u|
|
90
|
-
u[:unit] == "unknown" || u[:prefix] == "unknown" || u[:unit].nil?
|
91
|
-
end
|
92
|
-
|
93
|
-
norm.map do |u|
|
94
|
-
{ dimension: U2D[u[:unit]][:dimension],
|
95
|
-
unit: u[:unit],
|
96
|
-
exponent: u[:exponent] || 1,
|
97
|
-
symbol: U2D[u[:unit]][:symbol] }
|
98
|
-
end.sort { |a, b| U2D[a[:unit]][:order] <=> U2D[b[:unit]][:order] }
|
99
|
-
end
|
100
|
-
|
101
|
-
def dimension1(dim)
|
102
|
-
%(<#{dim[:dimension]} symbol="#{dim[:symbol]}"
|
103
|
-
powerNumerator="#{float_to_display(dim[:exponent])}"/>)
|
104
|
-
end
|
105
|
-
|
106
|
-
def dim_id(dims)
|
107
|
-
return nil if dims.nil? || dims.empty?
|
108
|
-
|
109
|
-
dimhash = dims.each_with_object({}) { |h, m| m[h[:dimension]] = h }
|
110
|
-
dimsvector = %w(Length Mass Time ElectricCurrent ThermodynamicTemperature
|
111
|
-
AmountOfSubstance LuminousIntensity PlaneAngle)
|
112
|
-
.map { |h| dimhash.dig(h, :exponent) }.join(":")
|
113
|
-
id = @dimensions_id&.values&.select { |d| d.vector == dimsvector }
|
114
|
-
&.first&.id and return id.to_s
|
115
|
-
"D_" + dims.map do |d|
|
116
|
-
U2D[d[:unit]][:symbol] +
|
117
|
-
(d[:exponent] == 1 ? "" : float_to_display(d[:exponent]))
|
118
|
-
end.join("")
|
119
|
-
end
|
120
|
-
|
121
65
|
def decompose_units(units)
|
122
66
|
gather_units(units_only(units).map { |u| decompose_unit(u) }.flatten)
|
123
67
|
end
|
124
68
|
|
125
69
|
def gather_units(units)
|
70
|
+
if units[0][:dim] then gather_dimensions(units)
|
71
|
+
else gather_units1(units)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def gather_units1(units)
|
126
76
|
units.sort_by { |a| a[:unit] }.each_with_object([]) do |k, m|
|
127
77
|
if m.empty? || m[-1][:unit] != k[:unit] then m << k
|
128
78
|
else
|
@@ -138,12 +88,24 @@ module Asciimath2UnitsML
|
|
138
88
|
end
|
139
89
|
end
|
140
90
|
|
91
|
+
def gather_dimensions(units)
|
92
|
+
units.sort_by { |a| a[:dim] }.each_with_object([]) do |k, m|
|
93
|
+
if m.empty? || m[-1][:dim] != k[:dim] then m << k
|
94
|
+
else
|
95
|
+
m[-1] = {
|
96
|
+
dim: m[-1][:dim],
|
97
|
+
exponent: (k[:exponent]&.to_f || 1) +
|
98
|
+
(m[-1][:exponent]&.to_f || 1),
|
99
|
+
}
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
141
104
|
# treat g not kg as base unit: we have stripped the prefix k in parsing
|
142
105
|
# reduce units down to basic units
|
143
106
|
def decompose_unit(u)
|
144
|
-
if u[:unit].nil?
|
145
|
-
|
146
|
-
elsif @units[u[:unit]].system_type == "SI_base" then u
|
107
|
+
if u[:unit].nil? || u[:unit] == "g" ||
|
108
|
+
@units[u[:unit]].system_type == "SI_base" then u
|
147
109
|
elsif !@units[u[:unit]].si_derived_bases
|
148
110
|
{ prefix: u[:prefix], unit: "unknown", exponent: u[:exponent] }
|
149
111
|
else
|
@@ -195,25 +157,6 @@ module Asciimath2UnitsML
|
|
195
157
|
XML
|
196
158
|
end
|
197
159
|
|
198
|
-
def dimid2dimensions(normtext)
|
199
|
-
@dimensions_id[normtext].keys.map do |k|
|
200
|
-
{ dimension: k,
|
201
|
-
symbol: U2D.values.select { |v| v[:dimension] == k }.first[:symbol],
|
202
|
-
exponent: @dimensions_id[normtext].exponent(k) }
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
def dimension(normtext)
|
207
|
-
return unless @units[normtext]&.dimension
|
208
|
-
|
209
|
-
dims = dimid2dimensions(@units[normtext]&.dimension)
|
210
|
-
<<~XML
|
211
|
-
<Dimension xmlns='#{UNITSML_NS}' xml:id="#{@units[normtext]&.dimension}">
|
212
|
-
#{dims.map { |u| dimension1(u) }.join("\n")}
|
213
|
-
</Dimension>
|
214
|
-
XML
|
215
|
-
end
|
216
|
-
|
217
160
|
def unitsml(units, origtext, normtext, quantity, name)
|
218
161
|
dims = units2dimensions(units)
|
219
162
|
<<~XML
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Asciimath2UnitsML
|
2
|
+
class Conv
|
3
|
+
def dimension_components(dims)
|
4
|
+
return if dims.nil? || dims.empty?
|
5
|
+
|
6
|
+
<<~XML
|
7
|
+
<Dimension xmlns='#{UNITSML_NS}' xml:id="#{dim_id(dims)}">
|
8
|
+
#{dims.map { |u| dimension1(u) }.join("\n")}
|
9
|
+
</Dimension>
|
10
|
+
XML
|
11
|
+
end
|
12
|
+
|
13
|
+
U2D = {
|
14
|
+
"m" => { dimension: "Length", order: 1, symbol: "L" },
|
15
|
+
"g" => { dimension: "Mass", order: 2, symbol: "M" },
|
16
|
+
"kg" => { dimension: "Mass", order: 2, symbol: "M" },
|
17
|
+
"s" => { dimension: "Time", order: 3, symbol: "T" },
|
18
|
+
"A" => { dimension: "ElectricCurrent", order: 4, symbol: "I" },
|
19
|
+
"K" => { dimension: "ThermodynamicTemperature", order: 5,
|
20
|
+
symbol: "Theta" },
|
21
|
+
"degK" => { dimension: "ThermodynamicTemperature", order: 5,
|
22
|
+
symbol: "Theta" },
|
23
|
+
"mol" => { dimension: "AmountOfSubstance", order: 6, symbol: "N" },
|
24
|
+
"cd" => { dimension: "LuminousIntensity", order: 7, symbol: "J" },
|
25
|
+
"deg" => { dimension: "PlaneAngle", order: 8, symbol: "phi" },
|
26
|
+
}.freeze
|
27
|
+
|
28
|
+
Dim2D = {
|
29
|
+
"dim_L" => U2D["m"],
|
30
|
+
"dim_M" => U2D["g"],
|
31
|
+
"dim_T" => U2D["s"],
|
32
|
+
"dim_I" => U2D["A"],
|
33
|
+
"dim_Theta" => U2D["K"],
|
34
|
+
"dim_N" => U2D["mol"],
|
35
|
+
"dim_J" => U2D["cd"],
|
36
|
+
"dim_phi" => U2D["deg"],
|
37
|
+
}.freeze
|
38
|
+
|
39
|
+
def units2dimensions(units)
|
40
|
+
norm = decompose_units(units)
|
41
|
+
return units2dimensions_dim_input(norm) if norm[0][:dim]
|
42
|
+
return if norm.any? do |u|
|
43
|
+
u[:unit] == "unknown" || u[:prefix] == "unknown" || u[:unit].nil?
|
44
|
+
end
|
45
|
+
|
46
|
+
norm.map do |u|
|
47
|
+
{ dimension: U2D[u[:unit]][:dimension],
|
48
|
+
unit: u[:unit],
|
49
|
+
exponent: u[:exponent] || 1,
|
50
|
+
symbol: U2D[u[:unit]][:symbol] }
|
51
|
+
end.sort { |a, b| U2D[a[:unit]][:order] <=> U2D[b[:unit]][:order] }
|
52
|
+
end
|
53
|
+
|
54
|
+
def units2dimensions_dim_input(norm)
|
55
|
+
norm.map do |u|
|
56
|
+
{ dimension: Dim2D[u[:dim]][:dimension],
|
57
|
+
exponent: u[:exponent] || 1,
|
58
|
+
id: u[:dim],
|
59
|
+
symbol: Dim2D[u[:dim]][:symbol] }
|
60
|
+
end.sort { |a, b| Dim2D[a[:id]][:order] <=> Dim2D[b[:id]][:order] }
|
61
|
+
end
|
62
|
+
|
63
|
+
def dimension1(dim)
|
64
|
+
%(<#{dim[:dimension]} symbol="#{dim[:symbol]}"
|
65
|
+
powerNumerator="#{float_to_display(dim[:exponent])}"/>)
|
66
|
+
end
|
67
|
+
|
68
|
+
def dim_id(dims)
|
69
|
+
return nil if dims.nil? || dims.empty?
|
70
|
+
|
71
|
+
dimhash = dims.each_with_object({}) { |h, m| m[h[:dimension]] = h }
|
72
|
+
dimsvector = %w(Length Mass Time ElectricCurrent ThermodynamicTemperature
|
73
|
+
AmountOfSubstance LuminousIntensity PlaneAngle)
|
74
|
+
.map { |h| dimhash.dig(h, :exponent) }.join(":")
|
75
|
+
id = @dimensions_id&.values&.select { |d| d.vector == dimsvector }
|
76
|
+
&.first&.id and return id.to_s
|
77
|
+
"D_" + dims.map do |d|
|
78
|
+
(U2D.dig(d[:unit], :symbol) || Dim2D.dig(d[:id], :symbol)) +
|
79
|
+
(d[:exponent] == 1 ? "" : float_to_display(d[:exponent]))
|
80
|
+
end.join("")
|
81
|
+
end
|
82
|
+
|
83
|
+
def decompose_units(units)
|
84
|
+
gather_units(units_only(units).map { |u| decompose_unit(u) }.flatten)
|
85
|
+
end
|
86
|
+
|
87
|
+
def dimid2dimensions(normtext)
|
88
|
+
@dimensions_id[normtext].keys.map do |k|
|
89
|
+
{ dimension: k,
|
90
|
+
symbol: U2D.values.select { |v| v[:dimension] == k }.first[:symbol],
|
91
|
+
exponent: @dimensions_id[normtext].exponent(k) }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def dimension(normtext)
|
96
|
+
return unless @units[normtext]&.dimension
|
97
|
+
|
98
|
+
dims = dimid2dimensions(@units[normtext]&.dimension)
|
99
|
+
<<~XML
|
100
|
+
<Dimension xmlns='#{UNITSML_NS}' xml:id="#{@units[normtext]&.dimension}">
|
101
|
+
#{dims.map { |u| dimension1(u) }.join("\n")}
|
102
|
+
</Dimension>
|
103
|
+
XML
|
104
|
+
end
|
105
|
+
|
106
|
+
def unitsml(units, origtext, normtext, quantity, name)
|
107
|
+
dims = units2dimensions(units)
|
108
|
+
<<~XML
|
109
|
+
#{unit(units, origtext, normtext, dims, name)}
|
110
|
+
#{prefix(units)}
|
111
|
+
#{dimension(normtext)}
|
112
|
+
#{dimension_components(dims)}
|
113
|
+
#{quantity(normtext, quantity)}
|
114
|
+
XML
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -1,102 +1,101 @@
|
|
1
1
|
module Asciimath2UnitsML
|
2
2
|
class Conv
|
3
3
|
include Rsec::Helpers
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
hash.each_with_object({}) do |(_k, v), m|
|
12
|
-
next if v.name.nil? || v.name.empty?
|
13
|
-
|
14
|
-
m[v.symbolid] = v
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def flip_name_and_symbols(hash)
|
19
|
-
hash.each_with_object({}) do |(_k, v), m|
|
20
|
-
next if v.name.nil? || v.name.empty?
|
21
|
-
|
22
|
-
v.symbolids.each { |s| m[s] = v }
|
23
|
-
end
|
4
|
+
def parsers
|
5
|
+
exponent = /\^\(-?\d+\)/.r.map { |m| m.sub(/\^/, "").gsub(/[()]/, "") } |
|
6
|
+
/\^-?\d+/.r.map { |m| m.sub(/\^/, "") }
|
7
|
+
multiplier = %r{\*|//|/}.r.map { |x| { multiplier: x[0] } }
|
8
|
+
units = units_parse(exponent, multiplier)
|
9
|
+
dimensions = dimensions_parser(exponent, multiplier)
|
10
|
+
[units.eof, dimensions.eof]
|
24
11
|
end
|
25
12
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
when Hash then symbolize_keys(value)
|
36
|
-
when Array then value.map { |m| symbolize_keys(m) }
|
37
|
-
else value
|
38
|
-
end
|
39
|
-
result[new_key] = new_value
|
40
|
-
result
|
41
|
-
end
|
13
|
+
def dimensions_parser(exponent, multiplier)
|
14
|
+
dim1 = /#{@dimensions.keys.sort_by(&:length).reverse.join("|")}/.r
|
15
|
+
dimension =
|
16
|
+
seq("sqrt(", dim1, ")") { |x| { dim: x[1], display_exponent: "0.5" } } |
|
17
|
+
seq(dim1, exponent._? & (multiplier | ")".r)) { |x| { dim: x[0], display_exponent: (x[1][0]) } } |
|
18
|
+
seq(dim1, exponent._?).eof { |x| { dim: x[0], display_exponent: (x[1][0]) } }
|
19
|
+
dimensions1 = "(".r >> lazy { dimensions } << ")" | dimension
|
20
|
+
dimensions = dimensions1.join(multiplier) # rubocop:disable Style/RedundantAssignment
|
21
|
+
dimensions
|
42
22
|
end
|
43
23
|
|
44
|
-
def
|
24
|
+
def units_parse(exponent, multiplier)
|
45
25
|
prefix2 = /#{@prefixes.keys.select { |x| x.size == 2 }.join("|")}/.r
|
46
26
|
prefix1 = /#{@prefixes.keys.select { |x| x.size == 1 }.join("|")}/.r
|
47
27
|
unit_keys = @units.keys.reject do |k|
|
48
28
|
/\*|\^|\/|^1$/.match(k) || @units[k].prefixed
|
49
29
|
end.map { |k| Regexp.escape(k) }
|
50
30
|
unit1 = /#{unit_keys.sort_by(&:length).reverse.join("|")}/.r
|
51
|
-
|
52
|
-
|
53
|
-
multiplier = %r{\*|//|/}.r.map { |x| { multiplier: x[0] } }
|
54
|
-
unit =
|
31
|
+
|
32
|
+
unit =
|
55
33
|
seq("sqrt(", unit1, ")") { |x| { prefix: nil, unit: x[1], display_exponent: "0.5" } } |
|
56
34
|
seq("sqrt(", prefix1, unit1, ")") { |x| { prefix: x[1], unit: x[2], display_exponent: "0.5" } } |
|
57
35
|
seq("sqrt(", prefix2, unit1, ")") { |x| { prefix: x[1], unit: x[2], display_exponent: "0.5" } } |
|
58
36
|
seq(unit1, exponent._? & (multiplier | ")".r)) { |x| { prefix: nil, unit: x[0], display_exponent: (x[1][0]) } } |
|
59
37
|
seq(unit1, exponent._?).eof { |x| { prefix: nil, unit: x[0], display_exponent: (x[1][0]) } } |
|
60
|
-
seq(prefix1, unit1, exponent._?
|
61
|
-
seq(prefix2, unit1, exponent._?
|
38
|
+
seq(prefix1, unit1, exponent._?) { |x| { prefix: x[0], unit: x[1], display_exponent: (x[2][0]) } } |
|
39
|
+
seq(prefix2, unit1, exponent._?) { |x| { prefix: x[0], unit: x[1], display_exponent: (x[2][0]) } } |
|
62
40
|
"1".r.map { |_| { prefix: nil, unit: "1", display_exponent: nil } }
|
63
|
-
units1 = "(".r >> lazy{units} << ")" | unit
|
64
|
-
units = seq(prefix2, "-") { |x| [{ prefix: x[0], unit: nil, display_exponent: nil }] } |
|
41
|
+
units1 = "(".r >> lazy { units } << ")" | unit
|
42
|
+
units = seq(prefix2, "-") { |x| [{ prefix: x[0], unit: nil, display_exponent: nil }] } | # rubocop:disable Style/RedundantAssignment
|
65
43
|
seq(prefix1, "-") { |x| [{ prefix: x[0], unit: nil, display_exponent: nil }] } |
|
66
44
|
units1.join(multiplier)
|
67
|
-
|
45
|
+
units
|
68
46
|
end
|
69
47
|
|
70
48
|
def parse(expr)
|
71
49
|
text = Array(expr.split(/,\s*/))
|
50
|
+
if /dim_/.match?(text[0]) then parse_dimensions(text)
|
51
|
+
else parse_units(text)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def parse_units(text)
|
72
56
|
units = @parser.parse!(text[0])
|
73
57
|
if !units || Rsec::INVALID[units]
|
74
58
|
raise Rsec::SyntaxError.new "error parsing UnitsML expression", x, 1, 0
|
75
59
|
end
|
76
60
|
|
77
61
|
Rsec::Fail.reset
|
78
|
-
postprocess(units, text)
|
62
|
+
postprocess(units, text, true)
|
63
|
+
end
|
64
|
+
|
65
|
+
def parse_dimensions(text)
|
66
|
+
units = @dim_parser.parse!(text[0])
|
67
|
+
if !units || Rsec::INVALID[units]
|
68
|
+
raise Rsec::SyntaxError.new "error parsing UnitsML expression", x, 1, 0
|
69
|
+
end
|
70
|
+
|
71
|
+
Rsec::Fail.reset
|
72
|
+
postprocess(units, text, false)
|
79
73
|
end
|
80
74
|
|
81
|
-
def postprocess(units, text)
|
75
|
+
def postprocess(units, text, is_units)
|
82
76
|
units = postprocess1(units.flatten)
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end&.first&.sub(/^multiplier:\s*/, "")
|
95
|
-
normtext = units_only(units).each.map do |u|
|
96
|
-
exp = u[:exponent] && u[:exponent] != "1" ? "^#{u[:exponent]}" : ""
|
97
|
-
"#{u[:prefix]}#{u[:unit]}#{exp}"
|
77
|
+
normtext = postprocess_normtext(units, is_units)
|
78
|
+
[units, text[0], normtext, postprocess_extr(text, "quantity"),
|
79
|
+
postprocess_extr(text, "name"), postprocess_extr(text, "symbol"),
|
80
|
+
postprocess_extr(text, "multiplier")]
|
81
|
+
end
|
82
|
+
|
83
|
+
def postprocess_normtext(units, is_units)
|
84
|
+
units_only(units).each.map do |u|
|
85
|
+
if is_units then "#{u[:prefix]}#{u[:unit]}#{display_exp(u)}"
|
86
|
+
else "#{u[:dim]}#{display_exp(u)}"
|
87
|
+
end
|
98
88
|
end.join("*")
|
99
|
-
|
89
|
+
end
|
90
|
+
|
91
|
+
def postprocess_extr(text, name)
|
92
|
+
text[1..-1]&.select do |x|
|
93
|
+
/^#{name}:/.match(x)
|
94
|
+
end&.first&.sub(/^#{name}:\s*/, "")
|
95
|
+
end
|
96
|
+
|
97
|
+
def display_exp(unit)
|
98
|
+
unit[:exponent] && unit[:exponent] != "1" ? "^#{unit[:exponent]}" : ""
|
100
99
|
end
|
101
100
|
|
102
101
|
def postprocess1(units)
|
@@ -128,8 +127,11 @@ module Asciimath2UnitsML
|
|
128
127
|
text = x.text.sub(%r{^unitsml\((.+)\)$}m, "\\1")
|
129
128
|
units, origtext, normtext, quantity, name, symbol, multiplier =
|
130
129
|
parse(text)
|
131
|
-
rendering =
|
132
|
-
|
130
|
+
rendering = if symbol
|
131
|
+
embeddedmathml(asciimath2mathml(symbol))
|
132
|
+
else
|
133
|
+
mathmlsymbol(units, false, multiplier)
|
134
|
+
end
|
133
135
|
x.replace("#{delimspace(rendering, x)}"\
|
134
136
|
"<mrow xref='#{unit_id(origtext)}'>#{rendering}</mrow>\n"\
|
135
137
|
"#{unitsml(units, origtext, normtext, quantity, name)}")
|
@@ -149,8 +151,10 @@ module Asciimath2UnitsML
|
|
149
151
|
|
150
152
|
text = HTMLEntities.new.encode(Nokogiri::XML("<mrow>#{rendering}</mrow>")
|
151
153
|
.text.strip)
|
152
|
-
/\p{L}|\p{N}/.match(text)
|
153
|
-
"<mo rspace='thickmathspace'>⁢</mo>"
|
154
|
+
if /\p{L}|\p{N}/.match?(text)
|
155
|
+
"<mo rspace='thickmathspace'>⁢</mo>"
|
156
|
+
else "<mo>⁢</mo>"
|
157
|
+
end
|
154
158
|
end
|
155
159
|
|
156
160
|
def dedup_ids(xml)
|
@@ -168,8 +172,8 @@ module Asciimath2UnitsML
|
|
168
172
|
end
|
169
173
|
|
170
174
|
def asciimath2mathml(expression)
|
171
|
-
AsciiMath::MathMLBuilder.new(:
|
172
|
-
AsciiMath.parse(HTMLEntities.new.decode(expression)).ast
|
175
|
+
AsciiMath::MathMLBuilder.new(msword: true).append_expression(
|
176
|
+
AsciiMath.parse(HTMLEntities.new.decode(expression)).ast,
|
173
177
|
).to_s.gsub(/<math>/, "<math xmlns='#{MATHML_NS}'>")
|
174
178
|
end
|
175
179
|
|
@@ -183,7 +187,7 @@ module Asciimath2UnitsML
|
|
183
187
|
def ambig_units
|
184
188
|
u = @units_id.each_with_object({}) do |(_k, v), m|
|
185
189
|
v.symbolids.each do |x|
|
186
|
-
next if %r{[*/^]}.match(x)
|
190
|
+
next if %r{[*/^]}.match?(x)
|
187
191
|
next unless v.symbols_hash[x][:html] != x
|
188
192
|
|
189
193
|
m[v.symbols_hash[x][:html]] ||= []
|
@@ -199,8 +203,10 @@ module Asciimath2UnitsML
|
|
199
203
|
u.each { |_, v| maxcols = v.size if maxcols < v.size }
|
200
204
|
puts %([cols="#{maxcols + 1}*"]\n|===\n|Symbol | Unit + ID #{'| ' * (maxcols - 1)}\n)
|
201
205
|
puts "\n\n"
|
202
|
-
u.keys.sort_by
|
203
|
-
.gsub(
|
206
|
+
u.keys.sort_by do |a|
|
207
|
+
[-u[a].size, a.gsub(%r{&[^;]+;}, "")
|
208
|
+
.gsub(/[^A-Za-z]/, "").downcase]
|
209
|
+
end.each do |k|
|
204
210
|
print "| #{html2adoc(k)} "
|
205
211
|
u[k].sort_by(&:size).each { |v1| print "| #{@units[v1].name}: `#{v1}` " }
|
206
212
|
puts "#{'| ' * (maxcols - u[k].size)}\n"
|