Class: Asciimath2UnitsML::Conv
- Inherits:
-
Object
- Object
- Asciimath2UnitsML::Conv
- Includes:
- Rsec::Helpers
- Defined in:
- lib/asciimath2unitsml/conv.rb,
lib/asciimath2unitsml/unit.rb,
lib/asciimath2unitsml/parse.rb,
lib/asciimath2unitsml/render.rb
Constant Summary collapse
- U2D =
{ "m" => { dimension: "Length", order: 1, symbol: "L" }, "g" => { dimension: "Mass", order: 2, symbol: "M" }, "kg" => { dimension: "Mass", order: 2, symbol: "M" }, "s" => { dimension: "Time", order: 3, symbol: "T" }, "A" => { dimension: "ElectricCurrent", order: 4, symbol: "I" }, "K" => { dimension: "ThermodynamicTemperature", order: 5, symbol: "Theta" }, "degK" => { dimension: "ThermodynamicTemperature", order: 5, symbol: "Theta" }, "mol" => { dimension: "AmountOfSubstance", order: 6, symbol: "N" }, "cd" => { dimension: "LuminousIntensity", order: 7, symbol: "J" }, "deg" => { dimension: "PlaneAngle", order: 8, symbol: "Phi" }, }.freeze
Instance Method Summary collapse
- #ambig_units ⇒ Object
- #asciimath2mathml(expression) ⇒ Object
- #Asciimath2UnitsML(expression) ⇒ Object
- #combine_prefixes(p1, p2) ⇒ Object
-
#compose_name(units, text) ⇒ Object
TODO: compose name from the component units.
-
#decompose_unit(u) ⇒ Object
treat g not kg as base unit: we have stripped the prefix k in parsing reduce units down to basic units.
- #decompose_units(units) ⇒ Object
- #dedup_ids(xml) ⇒ Object
- #delimspace(rendering, elem) ⇒ Object
- #dim_id(dims) ⇒ Object
- #dimension(normtext) ⇒ Object
- #dimension1(u) ⇒ Object
- #dimension_components(dims) ⇒ Object
- #dimid2dimensions(normtext) ⇒ Object
- #embeddedmathml(mathml) ⇒ Object
- #flip_name_and_symbol(hash) ⇒ Object
- #flip_name_and_symbols(hash) ⇒ Object
- #float_to_display(f) ⇒ Object
- #gather_units(units) ⇒ Object
- #html2adoc(k) ⇒ Object
- #htmlent(x) ⇒ Object
- #htmlsymbol(units, normalise) ⇒ Object
- #htmlsymbol_exponent(u, base) ⇒ Object
-
#initialize(options = {}) ⇒ Conv
constructor
A new instance of Conv.
-
#MathML2UnitsML(xml) ⇒ Object
www.w3.org/TR/mathml-units/ section 2: delimit number Invisible-Times unit.
- #mathmlsymbol(units, normalise) ⇒ Object
- #mathmlsymbol1(u, normalise) ⇒ Object
- #mathmlsymbol_exponent(u, base) ⇒ Object
- #mathmlsymbolwrap(units, normalise) ⇒ Object
- #multiplier(x) ⇒ Object
- #normalise_units(units) ⇒ Object
- #parse(x) ⇒ Object
- #parser ⇒ Object
- #postprocess(units, text) ⇒ Object
- #postprocess1(units) ⇒ Object
- #prefix(units) ⇒ Object
- #quantity(normtext, quantity) ⇒ Object
- #quantityname(id) ⇒ Object
- #read_yaml(path) ⇒ Object
- #render(unit, style) ⇒ Object
- #render_ambig_units(u) ⇒ Object
- #rootunits(units) ⇒ Object
- #symbol_key(v) ⇒ Object
- #symbolize_keys(hash) ⇒ Object
- #unit(units, origtext, normtext, dims, name) ⇒ Object
- #unit_id(text) ⇒ Object
- #unitname(units, text, name) ⇒ Object
- #units2dimensions(units) ⇒ Object
- #units_only(units) ⇒ Object
- #unitsml(units, origtext, normtext, quantity, name) ⇒ Object
- #unitsymbol(units) ⇒ Object
-
#unitsystem(units) ⇒ Object
kg exception.
- #validate_symbols(m, v) ⇒ Object
- #validate_unit(v) ⇒ Object
- #validate_unit_symbol_cardinality(us, k) ⇒ Object
- #validate_yaml(hash, path) ⇒ Object
Constructor Details
#initialize(options = {}) ⇒ Conv
Returns a new instance of Conv.
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/asciimath2unitsml/conv.rb', line 16 def initialize( = {}) @dimensions_id = read_yaml("../unitsdb/dimensions.yaml").each_with_object({}) do |(k, v), m| m[k.to_s] = UnitsDB::Dimension.new(k, v) end @prefixes_id = read_yaml("../unitsdb/prefixes.yaml").each_with_object({}) do |(k, v), m| m[k] = UnitsDB::Prefix.new(k, v) end @prefixes = flip_name_and_symbol(@prefixes_id) @quantities = read_yaml("../unitsdb/quantities.yaml").each_with_object({}) do |(k, v), m| m[k.to_s] = UnitsDB::Quantity.new(k, v) end @units_id = read_yaml("../unitsdb/units.yaml").each_with_object({}) do |(k, v), m| m[k.to_s] = UnitsDB::Unit.new(k.to_s, v) end @units = flip_name_and_symbols(@units_id) @symbols = @units.each_with_object({}) do |(k, v), m| v.symbolids.each { |x| m[x] = v.symbols_hash[x] } end @parser = parser @multiplier = multiplier([:multiplier] || "\u00b7") end |
Instance Method Details
#ambig_units ⇒ Object
196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/asciimath2unitsml/parse.rb', line 196 def ambig_units u = @units_id.each_with_object({}) do |(k, v), m| v.symbolids.each do |x| next if %r{[*/^]}.match(x) next unless v.symbols_hash[x][:html] != x m[v.symbols_hash[x][:html]] ||= [] m[v.symbols_hash[x][:html]] << x end end u.keys.each { |k| u[k] = u[k].unshift(k) if @symbols.dig(k, :html) == k } render_ambig_units(u) end |
#asciimath2mathml(expression) ⇒ Object
184 185 186 187 188 |
# File 'lib/asciimath2unitsml/parse.rb', line 184 def asciimath2mathml(expression) AsciiMath::MathMLBuilder.new(:msword => true).append_expression( AsciiMath.parse(HTMLEntities.new.decode(expression)).ast).to_s. gsub(/<math>/, "<math xmlns='#{MATHML_NS}'>") end |
#Asciimath2UnitsML(expression) ⇒ Object
146 147 148 149 |
# File 'lib/asciimath2unitsml/parse.rb', line 146 def Asciimath2UnitsML(expression) xml = Nokogiri::XML(asciimath2mathml(expression)) MathML2UnitsML(xml).to_xml end |
#combine_prefixes(p1, p2) ⇒ Object
139 140 141 142 143 144 145 146 147 148 |
# File 'lib/asciimath2unitsml/conv.rb', line 139 def combine_prefixes(p1, p2) return nil if p1.nil? && p2.nil? return p1.symbolid if p2.nil? return p2.symbolid if p1.nil? return "unknown" if p1.base != p2.base @prefixes.each do |_, p| return p.symbolid if p.base == p1.base && p.power == p1.power + p2.power end "unknown" end |
#compose_name(units, text) ⇒ Object
TODO: compose name from the component units
57 58 59 |
# File 'lib/asciimath2unitsml/unit.rb', line 57 def compose_name(units, text) text end |
#decompose_unit(u) ⇒ Object
treat g not kg as base unit: we have stripped the prefix k in parsing reduce units down to basic units
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/asciimath2unitsml/conv.rb', line 123 def decompose_unit(u) if u[:unit].nil? then u elsif u[:unit] == "g" then u elsif @units[u[:unit]].system_type == "SI_base" then u elsif !@units[u[:unit]].si_derived_bases { prefix: u[:prefix], unit: "unknown", exponent: u[:exponent] } else @units[u[:unit]].si_derived_bases.each_with_object([]) do |k, m| m << { prefix: !k[:prefix].nil? && !k[:prefix].empty? ? combine_prefixes(@prefixes_id[k[:prefix]], @prefixes[u[:prefix]]) : u[:prefix], unit: @units_id[k[:id]].symbolid, exponent: (k[:power]&.to_i || 1) * (u[:exponent]&.to_f || 1) } end end end |
#decompose_units(units) ⇒ Object
106 107 108 |
# File 'lib/asciimath2unitsml/conv.rb', line 106 def decompose_units(units) gather_units(units_only(units).map { |u| decompose_unit(u) }.flatten) end |
#dedup_ids(xml) ⇒ Object
172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/asciimath2unitsml/parse.rb', line 172 def dedup_ids(xml) %w(Unit Dimension Prefix Quantity).each do |t| xml.xpath(".//m:#{t}/@xml:id", "m" => UNITSML_NS).map { |a| a.text }.uniq.each do |v| xml.xpath(".//*[@xml:id = '#{v}']").each_with_index do |n, i| next if i == 0 n.remove end end end xml end |
#delimspace(rendering, elem) ⇒ Object
165 166 167 168 169 170 |
# File 'lib/asciimath2unitsml/parse.rb', line 165 def delimspace(rendering, elem) return "" if elem&.previous_element && elem&.previous_element.name != "mn" text = HTMLEntities.new.encode(Nokogiri::XML("<mrow>#{rendering}</mrow>").text.strip) /\p{L}|\p{N}/.match(text) ? "<mo rspace='thickmathspace'>⁢</mo>" : "<mo>⁢</mo>" end |
#dim_id(dims) ⇒ Object
94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/asciimath2unitsml/conv.rb', line 94 def dim_id(dims) return nil if dims.nil? || dims.empty? dimhash = dims.each_with_object({}) { |h, m| m[h[:dimension]] = h } dimsvector = %w(Length Mass Time ElectricCurrent ThermodynamicTemperature AmountOfSubstance LuminousIntensity PlaneAngle) .map { |h| dimhash.dig(h, :exponent) }.join(":") id = @dimensions_id&.values&.select { |d| d.vector == dimsvector }&.first&.id and return id.to_s "D_" + dims.map do |d| U2D[d[:unit]][:symbol] + (d[:exponent] == 1 ? "" : float_to_display(d[:exponent])) end.join("") end |
#dimension(normtext) ⇒ Object
177 178 179 180 181 182 183 184 185 |
# File 'lib/asciimath2unitsml/conv.rb', line 177 def dimension(normtext) return unless @units[normtext]&.dimension dims = dimid2dimensions(@units[normtext]&.dimension) <<~END <Dimension xmlns='#{UNITSML_NS}' xml:id="#{@units[normtext]&.dimension}"> #{dims.map { |u| dimension1(u) }.join("\n") } </Dimension> END end |
#dimension1(u) ⇒ Object
90 91 92 |
# File 'lib/asciimath2unitsml/conv.rb', line 90 def dimension1(u) %(<#{u[:dimension]} symbol="#{u[:symbol]}" powerNumerator="#{float_to_display(u[:exponent])}"/>) end |
#dimension_components(dims) ⇒ Object
57 58 59 60 61 62 63 64 |
# File 'lib/asciimath2unitsml/conv.rb', line 57 def dimension_components(dims) return if dims.nil? || dims.empty? <<~END <Dimension xmlns='#{UNITSML_NS}' xml:id="#{dim_id(dims)}"> #{dims.map { |u| dimension1(u) }.join("\n") } </Dimension> END end |
#dimid2dimensions(normtext) ⇒ Object
169 170 171 172 173 174 175 |
# File 'lib/asciimath2unitsml/conv.rb', line 169 def dimid2dimensions(normtext) @dimensions_id[normtext].keys.map do |k| { dimension: k, symbol: U2D.values.select { |v| v[:dimension] == k }.first[:symbol], exponent: @dimensions_id[normtext].exponent(k) } end end |
#embeddedmathml(mathml) ⇒ Object
190 191 192 193 194 |
# File 'lib/asciimath2unitsml/parse.rb', line 190 def (mathml) x = Nokogiri::XML(mathml) x.xpath(".//m:mi", "m" => MATHML_NS).each { |mi| mi["mathvariant"] = "normal" } x.children.to_xml end |
#flip_name_and_symbol(hash) ⇒ Object
9 10 11 12 13 14 |
# File 'lib/asciimath2unitsml/parse.rb', line 9 def flip_name_and_symbol(hash) hash.each_with_object({}) do |(k, v), m| next if v.name.nil? || v.name.empty? m[v.symbolid] = v end end |
#flip_name_and_symbols(hash) ⇒ Object
16 17 18 19 20 21 |
# File 'lib/asciimath2unitsml/parse.rb', line 16 def flip_name_and_symbols(hash) hash.each_with_object({}) do |(k, v), m| next if v.name.nil? || v.name.empty? v.symbolids.each { |s| m[s] = v } end end |
#float_to_display(f) ⇒ Object
38 39 40 |
# File 'lib/asciimath2unitsml/conv.rb', line 38 def float_to_display(f) ret = f.to_f.round(1).to_s.sub(/\.0$/, "") end |
#gather_units(units) ⇒ Object
110 111 112 113 114 115 116 117 118 119 |
# File 'lib/asciimath2unitsml/conv.rb', line 110 def gather_units(units) units.sort { |a, b| a[:unit] <=> b[:unit] }.each_with_object([]) do |k, m| if m.empty? || m[-1][:unit] != k[:unit] then m << k else m[-1] = { prefix: combine_prefixes(@prefixes[m[-1][:prefix]], @prefixes[k[:prefix]]), unit: m[-1][:unit], exponent: (k[:exponent]&.to_f || 1) + (m[-1][:exponent]&.to_f || 1) } end end end |
#html2adoc(k) ⇒ Object
222 223 224 225 226 |
# File 'lib/asciimath2unitsml/parse.rb', line 222 def html2adoc(k) k.gsub(%r{<i>}, "__").gsub(%r{</i>}, "__") .gsub(%r{<sup>}, "^").gsub(%r{</sup>}, "^") .gsub(%r{<sub>}, "~").gsub(%r{</sub>}, "~") end |
#htmlent(x) ⇒ Object
18 19 20 21 |
# File 'lib/asciimath2unitsml/render.rb', line 18 def htmlent(x) HTMLEntities.new.decode(x).split(/([<>&])/) .map { |c| /[<>'"]/.match(c) ? c : HTMLEntities.new.encode(c, :hexadecimal) }.join end |
#htmlsymbol(units, normalise) ⇒ Object
23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/asciimath2unitsml/render.rb', line 23 def htmlsymbol(units, normalise) units.map do |u| if u[:multiplier] then u[:multiplier] == "*" ? @multiplier[:html] : u[:multiplier] elsif u[:unit].nil? && u[:prefix] @prefixes[u[:prefix]].html else base = (u[:prefix] || "") + render(normalise ? @units[u[:unit]].symbolid : u[:unit], :html) htmlsymbol_exponent(u, base) end end.join("") end |
#htmlsymbol_exponent(u, base) ⇒ Object
35 36 37 38 39 40 41 42 43 |
# File 'lib/asciimath2unitsml/render.rb', line 35 def htmlsymbol_exponent(u, base) if u[:display_exponent] == "0.5" base = "√#{base}" elsif u[:display_exponent] exp = "<sup>#{u[:display_exponent].sub(/-/, "−")}</sup>" base += exp end base end |
#MathML2UnitsML(xml) ⇒ Object
www.w3.org/TR/mathml-units/ section 2: delimit number Invisible-Times unit
152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/asciimath2unitsml/parse.rb', line 152 def MathML2UnitsML(xml) xml.is_a? String and xml = Nokogiri::XML(xml) xml.xpath(".//m:mtext", "m" => MATHML_NS).each do |x| next unless %r{^unitsml\(.+\)$}.match(x.text) text = x.text.sub(%r{^unitsml\((.+)\)$}m, "\\1") units, origtext, normtext, quantity, name, symbol = parse(text) rendering = symbol ? (asciimath2mathml(symbol)) : mathmlsymbol(units, false) x.replace("#{delimspace(rendering, x)}<mrow xref='#{unit_id(origtext)}'>#{rendering}</mrow>\n"\ "#{unitsml(units, origtext, normtext, quantity, name)}") end dedup_ids(xml) end |
#mathmlsymbol(units, normalise) ⇒ Object
45 46 47 48 49 50 51 52 53 54 |
# File 'lib/asciimath2unitsml/render.rb', line 45 def mathmlsymbol(units, normalise) exp = units.map do |u| if u[:multiplier] then u[:multiplier] == "*" ? @multiplier[:mathml] : "<mo>#{u[:multiplier]}</mo>" elsif u[:unit].nil? && u[:prefix] %(<mi mathvariant='normal'>#{htmlent(@prefixes[u[:prefix]].html)}</mi>) else mathmlsymbol1(u, normalise) end end.join("") end |
#mathmlsymbol1(u, normalise) ⇒ Object
56 57 58 59 60 61 62 63 64 65 |
# File 'lib/asciimath2unitsml/render.rb', line 56 def mathmlsymbol1(u, normalise) base = render(normalise ? @units[u[:unit]].symbolid : u[:unit], :mathml) if u[:prefix] prefix = htmlent(@prefixes[u[:prefix]].html) base = base.match(/<mi mathvariant='normal'>/) ? base.sub(/<mi mathvariant='normal'>/, "<mi mathvariant='normal'>#{prefix}") : "<mrow><mi mathvariant='normal'>#{prefix}#{base}</mrow>" end mathmlsymbol_exponent(u, base) end |
#mathmlsymbol_exponent(u, base) ⇒ Object
67 68 69 70 71 72 73 74 75 |
# File 'lib/asciimath2unitsml/render.rb', line 67 def mathmlsymbol_exponent(u, base) if u[:display_exponent] == "0.5" base = "<msqrt>#{base}</msqrt>" elsif u[:display_exponent] exp = "<mn>#{u[:display_exponent]}</mn>".sub(/<mn>-/, "<mo>−</mo><mn>") base = "<msup><mrow>#{base}</mrow><mrow>#{exp}</mrow></msup>" end base end |
#mathmlsymbolwrap(units, normalise) ⇒ Object
77 78 79 80 81 |
# File 'lib/asciimath2unitsml/render.rb', line 77 def mathmlsymbolwrap(units, normalise) <<~END <math xmlns='#{MATHML_NS}'><mrow>#{mathmlsymbol(units, normalise)}</mrow></math> END end |
#multiplier(x) ⇒ Object
3 4 5 6 7 8 9 10 11 12 |
# File 'lib/asciimath2unitsml/render.rb', line 3 def multiplier(x) case x when :space { html: " ", mathml: "<mo rspace='thickmathspace'>⁢</mo>" } when :nospace { html: "", mathml: "<mo>⁢</mo>" } else { html: HTMLEntities.new.encode(x), mathml: "<mo>#{HTMLEntities.new.encode(x)}</mo>" } end end |
#normalise_units(units) ⇒ Object
27 28 29 30 31 32 33 34 |
# File 'lib/asciimath2unitsml/unit.rb', line 27 def normalise_units(units) units.map do |u| u1 = u.dup u1[:multiplier] and u1[:multiplier] = "*" u1[:exponent] and u1[:display_exponent] = u1[:exponent] u1 end end |
#parse(x) ⇒ Object
111 112 113 114 115 116 117 118 119 |
# File 'lib/asciimath2unitsml/parse.rb', line 111 def parse(x) text = Array(x.split(/,\s*/)) units = @parser.parse!(text[0]) if !units || Rsec::INVALID[units] raise Rsec::SyntaxError.new "error parsing UnitsML expression", x, 1, 0 end Rsec::Fail.reset postprocess(units, text) end |
#parser ⇒ Object
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/asciimath2unitsml/parse.rb', line 86 def parser prefix2 = /#{@prefixes.keys.select { |x| x.size == 2 }.join("|")}/.r prefix1 = /#{@prefixes.keys.select { |x| x.size == 1 }.join("|")}/.r unit_keys = @units.keys.reject do |k| /\*|\^|\/|^1$/.match(k) || @units[k].prefixed end.map { |k| Regexp.escape(k) } unit1 = /#{unit_keys.sort_by(&:length).reverse.join("|")}/.r exponent = /\^\(-?\d+\)/.r.map { |m| m.sub(/\^/, "").gsub(/[()]/, "") } | /\^-?\d+/.r.map { |m| m.sub(/\^/, "") } multiplier = %r{\*|//|/}.r.map { |x| { multiplier: x[0] } } unit = seq("sqrt(", unit1, ")") { |x| { prefix: nil, unit: x[1], display_exponent: "0.5" } } | seq("sqrt(", prefix1, unit1, ")") { |x| { prefix: x[1], unit: x[2], display_exponent: "0.5" } } | seq("sqrt(", prefix2, unit1, ")") { |x| { prefix: x[1], unit: x[2], display_exponent: "0.5" } } | seq(unit1, exponent._? & multiplier) { |x| { prefix: nil, unit: x[0], display_exponent: (x[1][0] )} } | seq(unit1, exponent._?).eof { |x| { prefix: nil, unit: x[0], display_exponent: (x[1][0] )} } | seq(prefix1, unit1, exponent._? ) { |x| { prefix: x[0], unit: x[1], display_exponent: (x[2][0] ) } } | seq(prefix2, unit1, exponent._? ) { |x| { prefix: x[0], unit: x[1], display_exponent: (x[2][0] ) } } | "1".r.map { |_| { prefix: nil, unit: "1", display_exponent: nil } } units = seq(prefix2, "-") { |x| [{ prefix: x[0], unit: nil, display_exponent: nil }] } | seq(prefix1, "-") { |x| [{ prefix: x[0], unit: nil, display_exponent: nil }] } | unit.join(multiplier) parser = units.eof end |
#postprocess(units, text) ⇒ Object
121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/asciimath2unitsml/parse.rb', line 121 def postprocess(units, text) units = postprocess1(units) quantity = text[1..-1]&.select { |x| /^quantity:/.match(x) }&.first&.sub(/^quantity:\s*/, "") name = text[1..-1]&.select { |x| /^name:/.match(x) }&.first&.sub(/^name:\s*/, "") symbol = text[1..-1]&.select { |x| /^symbol:/.match(x) }&.first&.sub(/^symbol:\s*/, "") normtext = units_only(units).each.map do |u| exp = u[:exponent] && u[:exponent] != "1" ? "^#{u[:exponent]}" : "" "#{u[:prefix]}#{u[:unit]}#{exp}" end.join("*") [units, text[0], normtext, quantity, name, symbol] end |
#postprocess1(units) ⇒ Object
133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/asciimath2unitsml/parse.rb', line 133 def postprocess1(units) inverse = false units.each_with_object([]) do |u, m| if u[:multiplier] inverse = (u[:multiplier] == "/") else u[:exponent] = inverse ? "-#{u[:display_exponent] || '1'}" : u[:display_exponent] u[:exponent] = u[:exponent]&.sub(/^--+/, "") end m << u end end |
#prefix(units) ⇒ Object
42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/asciimath2unitsml/conv.rb', line 42 def prefix(units) units.map { |u| u[:prefix] }.reject { |u| u.nil? }.uniq.map do |p| <<~END <Prefix xmlns='#{UNITSML_NS}' prefixBase='#{@prefixes[p].base}' prefixPower='#{@prefixes[p].power}' xml:id='#{@prefixes[p].id}'> <PrefixName xml:lang="en">#{@prefixes[p].name}</PrefixName> <PrefixSymbol type="ASCII">#{@prefixes[p].ascii}</PrefixSymbol> <PrefixSymbol type="unicode">#{@prefixes[p].unicode}</PrefixSymbol> <PrefixSymbol type="LaTeX">#{@prefixes[p].latex}</PrefixSymbol> <PrefixSymbol type="HTML">#{htmlent @prefixes[p].html}</PrefixSymbol> </Prefix> END end.join("\n") end |
#quantity(normtext, quantity) ⇒ Object
158 159 160 161 162 163 164 165 166 167 |
# File 'lib/asciimath2unitsml/conv.rb', line 158 def quantity(normtext, quantity) return unless @units[normtext] && @units[normtext].quantities.size == 1 || @quantities[quantity] id = quantity || @units[normtext].quantities.first dim = %( dimensionURL="##{@units[normtext].dimension}") if @units[normtext]&.dimension <<~END <Quantity xmlns='#{UNITSML_NS}' xml:id="#{id}"#{dim} quantityType="base"> #{quantityname(id)} </Quantity> END end |
#quantityname(id) ⇒ Object
150 151 152 153 154 155 156 |
# File 'lib/asciimath2unitsml/conv.rb', line 150 def quantityname(id) ret = "" @quantities[id].names.each do |q| ret += %(<QuantityName xml:lang="en-US">#{q}</QuantityName>) end ret end |
#read_yaml(path) ⇒ Object
5 6 7 |
# File 'lib/asciimath2unitsml/parse.rb', line 5 def read_yaml(path) validate_yaml(symbolize_keys(YAML.load_file(File.join(File.join(File.dirname(__FILE__), path)))), path) end |
#render(unit, style) ⇒ Object
14 15 16 |
# File 'lib/asciimath2unitsml/render.rb', line 14 def render(unit, style) @symbols[unit][style] || unit end |
#render_ambig_units(u) ⇒ Object
209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/asciimath2unitsml/parse.rb', line 209 def render_ambig_units(u) maxcols = 0 u.each { |_, v| maxcols = v.size if maxcols < v.size } puts %([cols="#{maxcols + 1}*"]\n|===\n|Symbol | Unit + ID #{"| " * (maxcols - 1)}\n) puts "\n\n" u.keys.sort_by { |a| [-u[a].size, a.gsub(%r{\&[^;]+;}, "").gsub(/[^A-Za-z]/, "").downcase] }.each do |k| print "| #{html2adoc(k)} " u[k].sort_by { |v1| v1.size }.each { |v1| print "| #{@units[v1].name}: `#{v1}` " } puts "#{"| " * (maxcols - u[k].size) }\n" end puts "|===\n" end |
#rootunits(units) ⇒ Object
68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/asciimath2unitsml/unit.rb', line 68 def rootunits(units) return if units_only(units).any? { |x| x[:unit].nil? } return if units.size == 1 && !units[0][:prefix] exp = units_only(units).map do |u| prefix = " prefix='#{u[:prefix]}'" if u[:prefix] exponent = " powerNumerator='#{u[:exponent]}'" if u[:exponent] && u[:exponent] != "1" "<EnumeratedRootUnit unit='#{@units[u[:unit]].name}'#{prefix}#{exponent}/>" end.join("\n") <<~END <RootUnits>#{exp}</RootUnits> END end |
#symbol_key(v) ⇒ Object
62 63 64 65 66 67 |
# File 'lib/asciimath2unitsml/parse.rb', line 62 def symbol_key(v) symbol = v[:unit_symbols]&.each_with_object([]) { |s, m| m << (s["id"] || s[:id]) } || v.dig(:symbol, :ascii) || v[:symbol] #|| v[:short] symbol = [symbol] if !symbol.nil? && v[:unit_symbols] && !symbol.is_a?(Array) symbol end |
#symbolize_keys(hash) ⇒ Object
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/asciimath2unitsml/parse.rb', line 69 def symbolize_keys(hash) return hash if hash.is_a? String hash.inject({}) do |result, (key, value)| new_key = case key when String then key.to_sym else key end new_value = case value when Hash then symbolize_keys(value) when Array then value.map { |m| symbolize_keys(m) } else value end result[new_key] = new_value result end end |
#unit(units, origtext, normtext, dims, name) ⇒ Object
13 14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/asciimath2unitsml/unit.rb', line 13 def unit(units, origtext, normtext, dims, name) return if units_only(units).any? { |x| x[:unit].nil? } dimid = dim_id(dims) norm_units = normalise_units(units) <<~END <Unit xmlns='#{UNITSML_NS}' xml:id='#{unit_id(normtext)}'#{dimid ? " dimensionURL='##{dimid}'" : ""}> #{unitsystem(units)} #{unitname(norm_units, normtext, name)} #{unitsymbol(norm_units)} #{rootunits(units)} </Unit> END end |
#unit_id(text) ⇒ Object
7 8 9 10 11 |
# File 'lib/asciimath2unitsml/unit.rb', line 7 def unit_id(text) text = text.gsub(/[()]/, "") /-$/.match(text) and return @prefixes[text.sub(/-$/, "")].id "U_" + (@units[text] ? @units[text].id.gsub(/'/, "_") : text.gsub(/\*/, ".").gsub(/\^/, "")) end |
#unitname(units, text, name) ⇒ Object
51 52 53 54 |
# File 'lib/asciimath2unitsml/unit.rb', line 51 def unitname(units, text, name) name ||= @units[text] ? @units[text].name : compose_name(units, text) "<UnitName xml:lang='en'>#{name}</UnitName>" end |
#units2dimensions(units) ⇒ Object
79 80 81 82 83 84 85 86 87 88 |
# File 'lib/asciimath2unitsml/conv.rb', line 79 def units2dimensions(units) norm = decompose_units(units) return if norm.any? { |u| u[:unit] == "unknown" || u[:prefix] == "unknown" || u[:unit].nil? } norm.map do |u| { dimension: U2D[u[:unit]][:dimension], unit: u[:unit], exponent: u[:exponent] || 1, symbol: U2D[u[:unit]][:symbol] } end.sort { |a, b| U2D[a[:unit]][:order] <=> U2D[b[:unit]][:order] } end |
#units_only(units) ⇒ Object
3 4 5 |
# File 'lib/asciimath2unitsml/unit.rb', line 3 def units_only(units) units.reject { |u| u[:multiplier] } end |
#unitsml(units, origtext, normtext, quantity, name) ⇒ Object
187 188 189 190 191 192 193 194 195 196 |
# File 'lib/asciimath2unitsml/conv.rb', line 187 def unitsml(units, origtext, normtext, quantity, name) dims = units2dimensions(units) <<~END #{unit(units, origtext, normtext, dims, name)} #{prefix(units)} #{dimension(normtext)} #{dimension_components(dims)} #{quantity(normtext, quantity)} END end |
#unitsymbol(units) ⇒ Object
61 62 63 64 65 66 |
# File 'lib/asciimath2unitsml/unit.rb', line 61 def unitsymbol(units) <<~END <UnitSymbol type="HTML">#{htmlsymbol(units, true)}</UnitSymbol> <UnitSymbol type="MathML">#{mathmlsymbolwrap(units, true)}</UnitSymbol> END end |
#unitsystem(units) ⇒ Object
kg exception
37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/asciimath2unitsml/unit.rb', line 37 def unitsystem(units) return if units_only(units).any? { |x| x[:unit].nil? } ret = [] units = units_only(units) units.any? { |x| @units[x[:unit]].system_name != "SI" } and ret << "<UnitSystem name='not_SI' type='not_SI' xml:lang='en-US'/>" if units.any? { |x| @units[x[:unit]].system_name == "SI" } base = units.size == 1 && @units[units[0][:unit]].system_type == "SI-base" base = true if units.size == 1 && units[0][:unit] == "g" && units[0][:prefix] == "k" ret << "<UnitSystem name='SI' type='#{base ? "SI_base" : "SI_derived"}' xml:lang='en-US'/>" end ret.join("\n") end |
#validate_symbols(m, v) ⇒ Object
44 45 46 47 48 49 50 51 52 53 |
# File 'lib/asciimath2unitsml/parse.rb', line 44 def validate_symbols(m, v) symbol = symbol_key(v) !symbol.nil? or raise StandardError.new "No symbol provided for unit: #{v}" Array(symbol)&.each do |s| m[s] && s != "1" and raise StandardError.new "symbol #{s} is not unique in #{v}: already used for #{m[s]}" m[s] = v end m end |
#validate_unit(v) ⇒ Object
34 35 36 37 38 39 40 41 42 |
# File 'lib/asciimath2unitsml/parse.rb', line 34 def validate_unit(v) if v[:quantity_reference] v[:quantity_reference].is_a?(Array) or raise StandardError.new "No quantity_reference array provided for unit: #{v}" end if v[:unit_name] v[:unit_name].is_a?(Array) or raise StandardError.new "No unit_name array provided for unit: #{v}" end end |
#validate_unit_symbol_cardinality(us, k) ⇒ Object
55 56 57 58 59 60 |
# File 'lib/asciimath2unitsml/parse.rb', line 55 def validate_unit_symbol_cardinality(us, k) return true if us.nil? !us[:id].nil? && !us[:ascii].nil? && !us[:html].nil? && !us[:mathml].nil? && !us[:latex].nil? && !us[:unicode].nil? and return true raise StandardError.new "malformed unit_symbol for #{k}: #{us}" end |
#validate_yaml(hash, path) ⇒ Object
23 24 25 26 27 28 29 30 31 32 |
# File 'lib/asciimath2unitsml/parse.rb', line 23 def validate_yaml(hash, path) return hash if path == "../unitsdb/quantities.yaml" return hash if path == "../unitsdb/dimensions.yaml" hash.each_with_object({}) do |(k, v), m| path == "../unitsdb/units.yaml" and validate_unit(v) m = validate_symbols(m, v) v[:unit_symbols]&.each { |s| validate_unit_symbol_cardinality(s, k) } end hash end |