asciimath2unitsml 0.3.3 → 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.
- 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"
|