module Asciimath2UnitsML class Conv include Rsec::Helpers def read_yaml(path) symbolize_keys(YAML.load_file(File.join(File.join(File.dirname(__FILE__), path)))) end def flip_name_and_id(yaml) yaml.each_with_object({}) do |(k, v), m| next if v[:name].nil? || v[:name].empty? symbol = v[:symbol] || v[:short] m[symbol.to_sym] = v m[symbol.to_sym][:symbol] = symbol m[symbol.to_sym][:id] = k.to_s end end def symbolize_keys(hash) 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) else value end result[new_key] = new_value result end end def parser prefix = /#{@prefixes.keys.join("|")}/.r unit_keys = @units.keys.reject do |k| @units[k][:type]&.include?("buildable") || /\*|\^/.match(k) 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(unit1, exponent._?) { |x| { prefix: nil, unit: x[0], display_exponent: (x[1][0] )} } | seq(prefix, unit1, exponent._?) { |x| { prefix: x[0][0], unit: x[1], display_exponent: (x[2][0] ) } } units_tail = seq(multiplier, unit) { |x| [x[0], x[1]] } units = seq(unit, units_tail.star) { |x| [x[0], x[1]].flatten } parser = units.eof end def parse(x) units = @parser.parse(x) if !units || Rsec::INVALID[units] raise Rsec::SyntaxError.new "error parsing UnitsML expression", x, 1, 0 end Rsec::Fail.reset postprocess(units, x) end def postprocess(units, text) units = postprocess1(units) 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, normtext] end 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 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" }, "mol" => { dimension: "AmountOfSubstance", order: 6, symbol: "N" }, "cd" => { dimension: "LuminousIntensity", order: 7, symbol: "J" }, } def Asciimath2UnitsML(expression) xml = Nokogiri::XML(asciimath2mathml(expression)) MathML2UnitsML(xml).to_xml end # https://www.w3.org/TR/mathml-units/ section 2: delimit number Invisible-Times unit 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 = parse(text) delim = x&.previous_element&.name == "mn" ? "" : "" x.replace("#{delim}#{mathmlsymbol(units)}\n"\ "#{unitsml(units, origtext, normtext)}") end xml end def asciimath2mathml(expression) AsciiMath::MathMLBuilder.new(:msword => true).append_expression( AsciiMath.parse(HTMLEntities.new.decode(expression)).ast).to_s. gsub(//, "") end end end