plurimath 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/.rspec +3 -0
  4. data/.rspec-opal +11 -0
  5. data/Gemfile +3 -0
  6. data/Rakefile +11 -0
  7. data/lib/plurimath/asciimath/transform.rb +15 -0
  8. data/lib/plurimath/latex/constants.rb +3 -0
  9. data/lib/plurimath/latex/parse.rb +20 -11
  10. data/lib/plurimath/latex/transform.rb +24 -2
  11. data/lib/plurimath/math/core.rb +88 -0
  12. data/lib/plurimath/math/formula.rb +68 -24
  13. data/lib/plurimath/math/function/base.rb +8 -2
  14. data/lib/plurimath/math/function/binary_function.rb +36 -4
  15. data/lib/plurimath/math/function/color.rb +14 -0
  16. data/lib/plurimath/math/function/fenced.rb +27 -0
  17. data/lib/plurimath/math/function/floor.rb +1 -1
  18. data/lib/plurimath/math/function/font_style.rb +45 -0
  19. data/lib/plurimath/math/function/frac.rb +6 -0
  20. data/lib/plurimath/math/function/int.rb +7 -0
  21. data/lib/plurimath/math/function/left.rb +19 -1
  22. data/lib/plurimath/math/function/lim.rb +6 -0
  23. data/lib/plurimath/math/function/limits.rb +7 -0
  24. data/lib/plurimath/math/function/log.rb +6 -0
  25. data/lib/plurimath/math/function/menclose.rb +6 -0
  26. data/lib/plurimath/math/function/mod.rb +6 -0
  27. data/lib/plurimath/math/function/msgroup.rb +28 -0
  28. data/lib/plurimath/math/function/multiscript.rb +7 -0
  29. data/lib/plurimath/math/function/nary.rb +94 -0
  30. data/lib/plurimath/math/function/oint.rb +6 -0
  31. data/lib/plurimath/math/function/over.rb +6 -0
  32. data/lib/plurimath/math/function/overset.rb +6 -0
  33. data/lib/plurimath/math/function/power.rb +8 -2
  34. data/lib/plurimath/math/function/power_base.rb +10 -31
  35. data/lib/plurimath/math/function/prod.rb +19 -18
  36. data/lib/plurimath/math/function/right.rb +19 -1
  37. data/lib/plurimath/math/function/root.rb +6 -0
  38. data/lib/plurimath/math/function/rule.rb +7 -0
  39. data/lib/plurimath/math/function/semantics.rb +6 -0
  40. data/lib/plurimath/math/function/stackrel.rb +6 -0
  41. data/lib/plurimath/math/function/substack.rb +6 -0
  42. data/lib/plurimath/math/function/sum.rb +26 -25
  43. data/lib/plurimath/math/function/table.rb +52 -24
  44. data/lib/plurimath/math/function/td.rb +28 -0
  45. data/lib/plurimath/math/function/ternary_function.rb +44 -4
  46. data/lib/plurimath/math/function/text.rb +24 -2
  47. data/lib/plurimath/math/function/tr.rb +28 -0
  48. data/lib/plurimath/math/function/unary_function.rb +43 -3
  49. data/lib/plurimath/math/function/underover.rb +7 -55
  50. data/lib/plurimath/math/function/underset.rb +6 -0
  51. data/lib/plurimath/math/function/vec.rb +40 -0
  52. data/lib/plurimath/math/function.rb +7 -5
  53. data/lib/plurimath/math/number.rb +9 -5
  54. data/lib/plurimath/math/symbol.rb +13 -9
  55. data/lib/plurimath/math.rb +1 -3
  56. data/lib/plurimath/mathml/parser.rb +4 -4
  57. data/lib/plurimath/mathml/transform.rb +3 -4
  58. data/lib/plurimath/omml/parser.rb +3 -3
  59. data/lib/plurimath/omml/transform.rb +12 -11
  60. data/lib/plurimath/setup/oga.rb +5 -0
  61. data/lib/plurimath/setup/opal.rb.erb +8 -0
  62. data/lib/plurimath/setup/ox.rb +5 -0
  63. data/lib/plurimath/utility.rb +48 -13
  64. data/lib/plurimath/version.rb +1 -1
  65. data/lib/plurimath/xml_engine/oga.rb +246 -0
  66. data/lib/plurimath/xml_engine/ox.rb +29 -0
  67. data/lib/plurimath/xml_engine.rb +6 -0
  68. data/lib/plurimath.rb +12 -2
  69. metadata +11 -2
@@ -88,8 +88,7 @@ module Plurimath
88
88
 
89
89
  rule(merror: sequence(:merror)) do
90
90
  Math::Function::Merror.new(
91
- merror[0],
92
- merror[1],
91
+ Utility.filter_values(merror),
93
92
  )
94
93
  end
95
94
 
@@ -247,7 +246,7 @@ module Plurimath
247
246
  symbols = Constants::UNICODE_SYMBOLS.transform_keys(&:to_s)
248
247
  text = entities.encode(mtext.flatten.join, :hexadecimal)
249
248
  symbols.each do |code, string|
250
- text.gsub!(code.downcase, "unicode[:#{string}]")
249
+ text = text.gsub(code.downcase, "unicode[:#{string}]")
251
250
  end
252
251
  Math::Function::Text.new(text)
253
252
  end
@@ -257,7 +256,7 @@ module Plurimath
257
256
  symbols = Constants::UNICODE_SYMBOLS.transform_keys(&:to_s)
258
257
  text = entities.encode(ms.first, :hexadecimal)
259
258
  symbols.each do |code, string|
260
- text.gsub!(code.downcase, "unicode[:#{string}]")
259
+ text = text.gsub(code.downcase, "unicode[:#{string}]")
261
260
  end
262
261
  Math::Function::Text.new(text)
263
262
  end
@@ -33,7 +33,7 @@ module Plurimath
33
33
  end
34
34
 
35
35
  def parse
36
- nodes = Ox.load(text, strip_namespace: true)
36
+ nodes = Plurimath.xml_engine.load(text)
37
37
  @hash = { sequence: parse_nodes(nodes.nodes) }
38
38
  nodes = JSON.parse(@hash.to_json, symbolize_names: true)
39
39
  Math::Formula.new(
@@ -71,14 +71,14 @@ module Plurimath
71
71
  end
72
72
 
73
73
  def organize_table_td(node)
74
- node.locate("e/?").each do |child_node|
74
+ node.locate("e/*").each do |child_node|
75
75
  child_node.name = "mtd" if child_node.name == "r"
76
76
  end
77
77
  end
78
78
 
79
79
  def organize_fonts(node)
80
80
  attrs_arr = { val: [] }
81
- node.locate("rPr/?").each do |child|
81
+ node.locate("rPr/*").each do |child|
82
82
  attrs_arr[:val] << child.attributes["val"]
83
83
  end
84
84
  node.attributes.merge! attrs_arr
@@ -98,11 +98,17 @@ module Plurimath
98
98
  open_paren = fenced.shift if fenced&.first&.class_name == "symbol"
99
99
  close_paren = fenced.shift if fenced&.first&.class_name == "symbol"
100
100
  fenced_value = fenced.compact
101
- Math::Function::Fenced.new(
102
- open_paren,
103
- fenced_value,
104
- close_paren,
105
- )
101
+ if fenced_value.length == 1 && fenced_value.first.is_a?(Math::Function::Table)
102
+ fenced_value.first.open_paren = open_paren&.value
103
+ fenced_value.first.close_paren = close_paren&.value
104
+ fenced_value
105
+ else
106
+ Math::Function::Fenced.new(
107
+ open_paren,
108
+ fenced_value,
109
+ close_paren,
110
+ )
111
+ end
106
112
  end
107
113
 
108
114
  rule(dPr: subtree(:dpr)) do
@@ -176,12 +182,7 @@ module Plurimath
176
182
  ternary_class.parameter_three = Utility.filter_values(nary[3])
177
183
  ternary_class
178
184
  else
179
- Math::Formula.new(
180
- [
181
- Utility.nary_fonts(nary),
182
- Utility.filter_values(nary[3]),
183
- ],
184
- )
185
+ Utility.nary_fonts(nary)
185
186
  end
186
187
  end
187
188
 
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "plurimath/xml_engine/oga"
4
+
5
+ Plurimath.xml_engine = Plurimath::XMLEngine::Oga
@@ -0,0 +1,8 @@
1
+ require 'plurimath/math/core'
2
+ require 'plurimath/math/function'
3
+ <% (
4
+ Dir[File.dirname(__dir__)+"/math/function/*.rb"] +
5
+ Dir[File.dirname(__dir__)+"/math/function/**/*.rb"]
6
+ ).each do |f| %>
7
+ require 'plurimath/<%= f.split("lib/plurimath").last.gsub(/.rb$/,'') %>'
8
+ <% end %>
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "plurimath/xml_engine/ox"
4
+
5
+ Plurimath.xml_engine = Plurimath::XMLEngine::Ox
@@ -91,6 +91,12 @@ module Plurimath
91
91
  "{": "}",
92
92
  "[": "]",
93
93
  }.freeze
94
+ TEXT_CLASSES = %w[
95
+ unicode
96
+ symbol
97
+ number
98
+ text
99
+ ].freeze
94
100
 
95
101
  class << self
96
102
  def organize_table(array, column_align: nil, options: nil)
@@ -187,7 +193,7 @@ module Plurimath
187
193
  def ox_element(node, attributes: [], namespace: "")
188
194
  namespace = "#{namespace}:" unless namespace.empty?
189
195
 
190
- element = Ox::Element.new("#{namespace}#{node}")
196
+ element = Plurimath.xml_engine.new_element("#{namespace}#{node}")
191
197
  attributes&.each do |attr_key, attr_value|
192
198
  element[attr_key] = attr_value
193
199
  end
@@ -249,12 +255,14 @@ module Plurimath
249
255
 
250
256
  def nary_fonts(nary)
251
257
  narypr = nary.first.flatten.compact
252
- subsup = narypr.any?("undOvr") ? "underover" : "power_base"
258
+ subsup = narypr.any?("undOvr") ? "undOvr" : "subSup"
253
259
  unicode = narypr.any?(Hash) ? narypr.first[:chr] : "∫"
254
- get_class(subsup).new(
260
+ Math::Function::Nary.new(
255
261
  Math::Symbol.new(string_to_html_entity(unicode)),
256
- nary[1],
257
- nary[2],
262
+ filter_values(nary[1]),
263
+ filter_values(nary[2]),
264
+ filter_values(nary[3]),
265
+ { type: subsup }
258
266
  )
259
267
  end
260
268
 
@@ -283,7 +291,8 @@ module Plurimath
283
291
  end
284
292
 
285
293
  def td_value(td_object)
286
- if td_object.is_a?(String) && td_object.empty?
294
+ str_classes = [String, Parslet::Slice]
295
+ if str_classes.include?(td_object.class) && td_object.to_s.empty?
287
296
  return Math::Function::Text.new(nil)
288
297
  end
289
298
 
@@ -452,7 +461,7 @@ module Plurimath
452
461
  end
453
462
 
454
463
  def left_right_objects(paren, function)
455
- paren = if paren.to_s.match?(/\\{|\\}/)
464
+ paren = if paren.to_s.match?(/\\\{|\\\}/)
456
465
  paren.to_s.gsub(/\\/, "")
457
466
  else
458
467
  Latex::Constants::LEFT_RIGHT_PARENTHESIS[paren.to_sym]
@@ -469,12 +478,7 @@ module Plurimath
469
478
  def mrow_left_right(mrow = [])
470
479
  object = mrow.first
471
480
  !(
472
- (
473
- (
474
- object.is_a?(Math::Function::TernaryFunction) && object.any_value_exist?
475
- ) &&
476
- (mrow.length <= 2)
477
- ) ||
481
+ ((object.is_a?(Math::Function::TernaryFunction) && object.any_value_exist?) && (mrow.length <= 2)) ||
478
482
  (object.is_a?(Math::Function::UnaryFunction) && mrow.length == 1)
479
483
  )
480
484
  end
@@ -564,6 +568,37 @@ module Plurimath
564
568
  end
565
569
  end
566
570
  end
571
+
572
+ def validate_math_zone(object)
573
+ return false unless object
574
+
575
+ if object.is_a?(Math::Formula)
576
+ filter_math_zone_values(object.value).find do |d|
577
+ !d.is_a?(Math::Function::Text)
578
+ end
579
+ else
580
+ !TEXT_CLASSES.include?(object.class_name)
581
+ end
582
+ end
583
+
584
+ def filter_math_zone_values(value)
585
+ return [] if value&.empty?
586
+
587
+ new_arr = []
588
+ temp_array = []
589
+ skip_index = nil
590
+ value.each_with_index do |obj, index|
591
+ object = obj.dup
592
+ next if index == skip_index
593
+ next temp_array << object.value if TEXT_CLASSES.include?(object.class_name)
594
+
595
+ new_arr << Math::Function::Text.new(temp_array.join(" ")) if temp_array.any?
596
+ temp_array = []
597
+ new_arr << object
598
+ end
599
+ new_arr << Math::Function::Text.new(temp_array.join(" ")) if temp_array.any?
600
+ new_arr
601
+ end
567
602
  end
568
603
  end
569
604
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Plurimath
4
- VERSION = "0.5.0"
4
+ VERSION = "0.6.0"
5
5
  end
@@ -0,0 +1,246 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "plurimath/xml_engine"
4
+ require "corelib/array/pack" if RUBY_ENGINE == "opal"
5
+ require "oga"
6
+
7
+ module Plurimath
8
+ module XMLEngine
9
+ class Oga
10
+ class << self
11
+ def new_element(name)
12
+ data = ::Oga::XML::Element.new(name: name)
13
+ Node.new(data)
14
+ end
15
+
16
+ def dump(data, indent: nil)
17
+ Dumper.new(data, indent: indent).dump.out
18
+ end
19
+
20
+ def load(data)
21
+ data = ::Oga::XML::Parser.new(data, html: true).parse
22
+ if data.xml_declaration
23
+ Document.new(data)
24
+ else
25
+ Document.new(data).nodes.first
26
+ end
27
+ end
28
+
29
+ def is_xml_comment?(node)
30
+ node = node.unwrap if node.respond_to? :unwrap
31
+ node.is_a?(Comment)
32
+ end
33
+ end
34
+
35
+ # Create API compatible with Ox, per Plurimath usage
36
+ class Wrapper
37
+ def initialize(value)
38
+ @wrapped = value
39
+ end
40
+
41
+ def unwrap
42
+ @wrapped
43
+ end
44
+
45
+ def ==(other)
46
+ self.class == other.class &&
47
+ @wrapped.inspect == other.unwrap.inspect
48
+ end
49
+ end
50
+
51
+ class Node < Wrapper
52
+ # Ox removes text nodes that are whitespace-only.
53
+ # There exists a weird edge case on which Plurimath depends:
54
+ # <mi> <!-- xxx --> &#x3C0;<!--GREEK SMALL LETTER PI--> </mi>
55
+ # If the last text node of an element that does not contain other
56
+ # elements is a whitespace, it preserves it. The first one can be
57
+ # safely removed.
58
+ def nodes
59
+ children = @wrapped.children
60
+ length = children.length
61
+ preserve_last = true
62
+ children.map.with_index do |i,idx|
63
+ if preserve_last && idx == length-1 && i.is_a?(::Oga::XML::Text)
64
+ i.text
65
+ elsif i.is_a? ::Oga::XML::Text
66
+ remove_indentation(i)
67
+ elsif i.is_a? ::Oga::XML::Comment
68
+ Node.new(i)
69
+ else
70
+ preserve_last = false
71
+ Node.new(i)
72
+ end
73
+ end.compact
74
+ end
75
+
76
+ def [](attr)
77
+ attr = attr.to_s
78
+
79
+ @wrapped.attributes.each do |e|
80
+ return e.value if [e.name, e.name.split(":").last].include? attr
81
+ end
82
+
83
+ nil
84
+ end
85
+
86
+ def []=(attr, value)
87
+ # Here we tap into the internal representation due to some likely
88
+ # bug in Oga
89
+ attr = ::Oga::XML::Attribute.new(name: attr.to_s)
90
+ attr.element = @wrapped
91
+ attr.instance_variable_set(:@value, value.to_s)
92
+ attr.instance_variable_set(:@decoded, true)
93
+ @wrapped.attributes << attr
94
+ end
95
+
96
+ def <<(other)
97
+ other = other.unwrap if other.respond_to? :unwrap
98
+
99
+ case other
100
+ when String
101
+ text = other
102
+ # Here we tap into the internal representation due to some likely
103
+ # bug in Oga
104
+ other = ::Oga::XML::Text.new
105
+ other.instance_variable_set(:@from_plurimath, true)
106
+ other.instance_variable_set(:@text, text)
107
+ other.instance_variable_set(:@decoded, true)
108
+ end
109
+
110
+ @wrapped.children << other.dup
111
+ self
112
+ end
113
+
114
+ def attributes
115
+ @wrapped.attributes.to_h do |e|
116
+ [e.name.split(":").last, e.value]
117
+ end
118
+ end
119
+
120
+ def locate(xpath)
121
+ @wrapped.xpath(xpath).map do |i|
122
+ case i
123
+ when ::Oga::XML::Text
124
+ i.text
125
+ when ::Oga::XML::Attribute
126
+ i.value
127
+ else
128
+ Node.new(i)
129
+ end
130
+ end
131
+ end
132
+
133
+ def name
134
+ @wrapped.name
135
+ end
136
+
137
+ def name=(new_name)
138
+ @wrapped.name = new_name
139
+ end
140
+
141
+ private
142
+
143
+ def remove_indentation(text)
144
+ from_us = text.instance_variable_get(:@from_plurimath)
145
+ !from_us && text.text.strip == "" ? nil : text.text
146
+ end
147
+ end
148
+
149
+ class Document < Node
150
+ end
151
+
152
+ Comment = ::Oga::XML::Comment
153
+
154
+ # Dump the tree just as if we were Ox. This is a limited implementation.
155
+ class Dumper
156
+ def initialize(tree, indent: nil)
157
+ @tree = tree
158
+ @indent = indent
159
+ @depth = 0
160
+ @out = ""
161
+ end
162
+
163
+ def dump(node = @tree)
164
+ case node
165
+ when Node
166
+ nodes = node.nodes
167
+ if nodes.length == 0
168
+ line_break
169
+ @out += "<#{node.unwrap.name}#{dump_attrs(node)}/>"
170
+ else
171
+ line_break
172
+ @out += "<#{node.unwrap.name}#{dump_attrs(node)}>"
173
+ @depth += 1
174
+ nodes.each { |i| dump(i) }
175
+ @depth -= 1
176
+ line_break unless nodes.last.is_a?(::String)
177
+ @out += "</#{node.unwrap.name}>"
178
+ end
179
+ when ::String
180
+ @out += entities(node)
181
+ end
182
+
183
+ line_break if node.object_id == @tree.object_id
184
+
185
+ self
186
+ end
187
+
188
+ attr_reader :out
189
+
190
+ ORD_AMP="&".ord
191
+ ORD_LT="<".ord
192
+ ORD_GT=">".ord
193
+ ORD_APOS="'".ord
194
+ ORD_QUOT='"'.ord
195
+ ORD_NEWLINE="\n".ord
196
+ ORD_CARRIAGERETURN="\r".ord
197
+
198
+ def self.entities(text,attr=false)
199
+ text.to_s.chars.map(&:ord).map do |i|
200
+ if i == ORD_AMP
201
+ "&amp;"
202
+ elsif i == ORD_LT
203
+ "&lt;"
204
+ elsif i == ORD_GT
205
+ "&gt;"
206
+ elsif i == ORD_QUOT && attr
207
+ "&quot;"
208
+ elsif i == ORD_NEWLINE || i == ORD_CARRIAGERETURN
209
+ i.chr("utf-8")
210
+ elsif i < 0x20
211
+ "&#x#{i.to_s(16).rjust(4, "0")};"
212
+ else
213
+ i.chr("utf-8")
214
+ end
215
+ end.join
216
+ end
217
+
218
+ private
219
+
220
+ def dump_attrs(node)
221
+ node.unwrap.attributes.map do |i|
222
+ # Currently, this is not part of the contract. But in the future
223
+ # it may be needed to also handle namespaces:
224
+ #
225
+ # if i.namespace
226
+ # %{ #{i.namespace.name}:#{i.name}="#{attr_entities i.value}"}
227
+ %{ #{i.name}="#{attr_entities i.value}"}
228
+ end.join
229
+ end
230
+
231
+ def entities(text)
232
+ self.class.entities(text)
233
+ end
234
+
235
+ def attr_entities(text)
236
+ self.class.entities(text, true)
237
+ end
238
+
239
+ def line_break
240
+ @out += "\n"
241
+ @out += " " * (@indent * @depth) if @indent
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "plurimath/xml_engine"
4
+ require "ox"
5
+ Ox.default_options = { encoding: "UTF-8" }
6
+
7
+ module Plurimath
8
+ module XMLEngine
9
+ class Ox
10
+ class << self
11
+ def new_element(name)
12
+ ::Ox::Element.new(name)
13
+ end
14
+
15
+ def dump(data, **options)
16
+ ::Ox.dump(data, **options)
17
+ end
18
+
19
+ def load(data)
20
+ ::Ox.load(data, strip_namespace: true)
21
+ end
22
+
23
+ def is_xml_comment?(node)
24
+ node.is_a?(::Ox::Comment)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,6 @@
1
+ module Plurimath
2
+ singleton_class.attr_accessor :xml_engine
3
+
4
+ module XMLEngine
5
+ end
6
+ end
data/lib/plurimath.rb CHANGED
@@ -1,4 +1,14 @@
1
- # frozen_string_literal: true
2
1
 
3
2
  require_relative "plurimath/version"
4
- require_relative "plurimath/math"
3
+
4
+ # Select an XML engine
5
+ if RUBY_ENGINE == 'opal'
6
+ require "plurimath/setup/oga"
7
+ require "plurimath/setup/opal"
8
+ elsif ENV['PLURIMATH_OGA']
9
+ require "plurimath/setup/oga"
10
+ else
11
+ require "plurimath/setup/ox"
12
+ end
13
+
14
+ require "plurimath/math"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plurimath
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-09-08 00:00:00.000000000 Z
11
+ date: 2023-11-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parslet
@@ -49,6 +49,8 @@ files:
49
49
  - ".github/workflows/release.yml"
50
50
  - ".gitignore"
51
51
  - ".hound.yml"
52
+ - ".rspec"
53
+ - ".rspec-opal"
52
54
  - ".rubocop.yml"
53
55
  - AsciiMath-Supported-Data.adoc
54
56
  - CODE_OF_CONDUCT.md
@@ -146,6 +148,7 @@ files:
146
148
  - lib/plurimath/math/function/msgroup.rb
147
149
  - lib/plurimath/math/function/msline.rb
148
150
  - lib/plurimath/math/function/multiscript.rb
151
+ - lib/plurimath/math/function/nary.rb
149
152
  - lib/plurimath/math/function/norm.rb
150
153
  - lib/plurimath/math/function/obrace.rb
151
154
  - lib/plurimath/math/function/oint.rb
@@ -202,10 +205,16 @@ files:
202
205
  - lib/plurimath/omml.rb
203
206
  - lib/plurimath/omml/parser.rb
204
207
  - lib/plurimath/omml/transform.rb
208
+ - lib/plurimath/setup/oga.rb
209
+ - lib/plurimath/setup/opal.rb.erb
210
+ - lib/plurimath/setup/ox.rb
205
211
  - lib/plurimath/unicode.rb
206
212
  - lib/plurimath/unitsml.rb
207
213
  - lib/plurimath/utility.rb
208
214
  - lib/plurimath/version.rb
215
+ - lib/plurimath/xml_engine.rb
216
+ - lib/plurimath/xml_engine/oga.rb
217
+ - lib/plurimath/xml_engine/ox.rb
209
218
  - plurimath.gemspec
210
219
  homepage: https://github.com/plurimath/plurimath
211
220
  licenses: