asciimath 1.0.9 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +5 -5
  2. data/AST.adoc +457 -0
  3. data/CHANGELOG.adoc +12 -0
  4. data/Gemfile.lock +39 -0
  5. data/README.adoc +27 -3
  6. data/asciimath.gemspec +9 -5
  7. data/lib/asciimath.rb +5 -4
  8. data/lib/asciimath/ast.rb +456 -0
  9. data/lib/asciimath/cli.rb +4 -1
  10. data/lib/asciimath/color_table.rb +21 -0
  11. data/lib/asciimath/html.rb +126 -111
  12. data/lib/asciimath/latex.rb +386 -0
  13. data/lib/asciimath/markup.rb +478 -0
  14. data/lib/asciimath/mathml.rb +189 -87
  15. data/lib/asciimath/parser.rb +498 -343
  16. data/lib/asciimath/symbol_table.rb +25 -0
  17. data/lib/asciimath/version.rb +1 -1
  18. data/spec/ast.rb +144 -0
  19. data/spec/parser_spec.rb +592 -165
  20. data/spec/schema/mathml2/common/common-attribs.xsd +41 -0
  21. data/spec/schema/mathml2/common/math.xsd +126 -0
  22. data/spec/schema/mathml2/common/xlink-href.xsd +20 -0
  23. data/spec/schema/mathml2/content/arith.xsd +90 -0
  24. data/spec/schema/mathml2/content/calculus.xsd +146 -0
  25. data/spec/schema/mathml2/content/common-attrib.xsd +30 -0
  26. data/spec/schema/mathml2/content/constants.xsd +83 -0
  27. data/spec/schema/mathml2/content/constructs.xsd +260 -0
  28. data/spec/schema/mathml2/content/elementary-functions.xsd +117 -0
  29. data/spec/schema/mathml2/content/functions.xsd +73 -0
  30. data/spec/schema/mathml2/content/linear-algebra.xsd +173 -0
  31. data/spec/schema/mathml2/content/logic.xsd +53 -0
  32. data/spec/schema/mathml2/content/relations.xsd +55 -0
  33. data/spec/schema/mathml2/content/semantics.xsd +85 -0
  34. data/spec/schema/mathml2/content/sets.xsd +236 -0
  35. data/spec/schema/mathml2/content/statistics.xsd +136 -0
  36. data/spec/schema/mathml2/content/tokens.xsd +120 -0
  37. data/spec/schema/mathml2/content/vector-calculus.xsd +88 -0
  38. data/spec/schema/mathml2/mathml2.xsd +59 -0
  39. data/spec/schema/mathml2/presentation/action.xsd +44 -0
  40. data/spec/schema/mathml2/presentation/characters.xsd +37 -0
  41. data/spec/schema/mathml2/presentation/common-attribs.xsd +113 -0
  42. data/spec/schema/mathml2/presentation/common-types.xsd +103 -0
  43. data/spec/schema/mathml2/presentation/error.xsd +40 -0
  44. data/spec/schema/mathml2/presentation/layout.xsd +195 -0
  45. data/spec/schema/mathml2/presentation/scripts.xsd +186 -0
  46. data/spec/schema/mathml2/presentation/space.xsd +52 -0
  47. data/spec/schema/mathml2/presentation/style.xsd +69 -0
  48. data/spec/schema/mathml2/presentation/table.xsd +216 -0
  49. data/spec/schema/mathml2/presentation/tokens.xsd +124 -0
  50. data/spec/schema/mathml3/mathml3-common.xsd +99 -0
  51. data/spec/schema/mathml3/mathml3-content.xsd +684 -0
  52. data/spec/schema/mathml3/mathml3-presentation.xsd +2151 -0
  53. data/spec/schema/mathml3/mathml3-strict-content.xsd +186 -0
  54. data/spec/schema/mathml3/mathml3.xsd +9 -0
  55. metadata +102 -10
  56. data/.gitignore +0 -16
  57. data/.travis.yml +0 -18
@@ -1,5 +1,17 @@
1
1
  = Asciimath Changelog
2
2
 
3
+ == 2.0.0
4
+
5
+ Enhancements::
6
+
7
+ * The AsciiMath parser has been significantly improved and should now have reached feature parity with the original Javascript implementation.
8
+ * Issue #16: add initial LaTeX output support (@GarkGarcia)
9
+ * Issue #26: avoid generating `<mfenced>` tags in MathML output
10
+
11
+ Bug fixes::
12
+
13
+ * Issue #14: add support for additional symbols and functions that are supported by Asciimath JS
14
+
3
15
  == 1.0.9
4
16
 
5
17
  Bug fixes::
@@ -0,0 +1,39 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ asciimath (2.0.0.next1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.3)
10
+ mini_portile2 (2.4.0)
11
+ nokogiri (1.10.9)
12
+ mini_portile2 (~> 2.4.0)
13
+ rake (13.0.1)
14
+ rspec (3.9.0)
15
+ rspec-core (~> 3.9.0)
16
+ rspec-expectations (~> 3.9.0)
17
+ rspec-mocks (~> 3.9.0)
18
+ rspec-core (3.9.2)
19
+ rspec-support (~> 3.9.3)
20
+ rspec-expectations (3.9.1)
21
+ diff-lcs (>= 1.2.0, < 2.0)
22
+ rspec-support (~> 3.9.0)
23
+ rspec-mocks (3.9.1)
24
+ diff-lcs (>= 1.2.0, < 2.0)
25
+ rspec-support (~> 3.9.0)
26
+ rspec-support (3.9.3)
27
+
28
+ PLATFORMS
29
+ ruby
30
+
31
+ DEPENDENCIES
32
+ asciimath!
33
+ bundler (> 0)
34
+ nokogiri (~> 1.10.9)
35
+ rake (~> 13.0.0)
36
+ rspec (~> 3.9.0)
37
+
38
+ BUNDLED WITH
39
+ 1.17.2
@@ -1,7 +1,7 @@
1
1
  # AsciiMath
2
2
  ifndef::env-site[:status:]
3
3
 
4
- An http://asciimath.org[AsciiMath] parser and MathML generator written in pure Ruby.
4
+ An http://asciimath.org[AsciiMath] parser and MathML/LaTeX generator written in pure Ruby.
5
5
 
6
6
  ifdef::status[]
7
7
  [discrete]
@@ -48,15 +48,16 @@ parsed_expression = AsciiMath.parse(asciimath)
48
48
 
49
49
  The parsed expression is a set of nested Array and Hash objects.
50
50
 
51
- This expression can then be converted to MathML or HTML (experimental.
51
+ This expression can then be converted to MathML, HTML (experimental) or LaTeX.
52
52
 
53
53
  [source,ruby]
54
54
  ----
55
55
  math_ml = parsed_expression.to_mathml
56
56
  html = parsed_expression.to_html
57
+ latex = parsed_expression.to_latex
57
58
  ----
58
59
 
59
- The MathML or HTML code is returned as a String.
60
+ The MathML, HTML or LaTeX code is returned as a String.
60
61
 
61
62
  ### Command line
62
63
 
@@ -78,6 +79,12 @@ asciimath mathml "an asciimath string"
78
79
  asciimath html "an asciimath string"
79
80
  ----
80
81
 
82
+ .LaTeX Generation
83
+ [source]
84
+ ----
85
+ asciimath latex "an asciimath string"
86
+ ----
87
+
81
88
  This command will print out the generated code on stdout.
82
89
 
83
90
 
@@ -95,6 +102,23 @@ Known issues are as follows:
95
102
  Rendering the HTML output correctly requires the inclusion of `style/math.css` in the html document.
96
103
  There is currently no specific required font for this output, it simply selects a `serif` font family - change the `@font-family` attribute in the `.math-inline` class to select something specific.
97
104
 
105
+ ## Notes on the LaTeX Output
106
+
107
+ All LaTeX commands and environments used in the output are coverved by
108
+ https://ctan.org/pkg/amsmath[`amsmath`] and `amssymb`, with a few exceptions:
109
+
110
+ * `\color`
111
+ * `\cancel`
112
+ * `\mathscr`
113
+ * `\twoheadrightarrowtail`
114
+
115
+ The `\color` command is supported by the
116
+ https://www.ctan.org/pkg/xcolor[`xcolor`] package, which is included in most
117
+ LaTeX distributions. The `\cancel` command is supported by the
118
+ https://www.ctan.org/pkg/cancel[cancel] package, also included in most LaTeX
119
+ distributions. The other commands are supported by the
120
+ https://ctan.org/pkg/stix[`stix`] package.
121
+
98
122
  ## Contributing
99
123
 
100
124
  . Fork it (https://github.com/pepijnve/asciimath/fork)
@@ -1,24 +1,28 @@
1
1
  # coding: utf-8
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
4
5
  require 'asciimath/version'
6
+ require 'rake/file_list'
5
7
 
6
8
  Gem::Specification.new do |spec|
7
9
  spec.name = "asciimath"
8
10
  spec.version = AsciiMath::VERSION
9
- spec.authors = ["Pepijn Van Eeckhoudt"]
10
- spec.email = ["pepijn@vaneeckhoudt.net"]
11
+ spec.authors = ["Pepijn Van Eeckhoudt", "Gark Garcia"]
12
+ spec.email = ["pepijn@vaneeckhoudt.net", "pablo-ecobar@riseup.net"]
11
13
  spec.summary = %q{AsciiMath parser and converter}
12
14
  spec.description = %q{A pure Ruby AsciiMath parsing and conversion library.}
13
15
  spec.homepage = ""
14
16
  spec.license = "MIT"
15
17
 
16
- spec.files = `git ls-files -z`.split("\x0")
18
+ spec.files = Rake::FileList['**/*'].exclude(*File.read('.gitignore').split)
19
+ .exclude('dump_symbol_table.rb')
17
20
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
21
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
22
  spec.require_paths = ["lib"]
20
23
 
21
24
  spec.add_development_dependency "bundler", "> 0"
22
- spec.add_development_dependency "rake", "~> 10.0"
23
- spec.add_development_dependency "rspec", "~> 3.1.0"
25
+ spec.add_development_dependency "rake", "~> 13.0.0"
26
+ spec.add_development_dependency "rspec", "~> 3.9.0"
27
+ spec.add_development_dependency "nokogiri", "~> 1.10.9"
24
28
  end
@@ -1,4 +1,5 @@
1
- require File.join(File.dirname(__FILE__), 'asciimath/version')
2
- require File.join(File.dirname(__FILE__), 'asciimath/parser')
3
- require File.join(File.dirname(__FILE__), 'asciimath/mathml')
4
- require File.join(File.dirname(__FILE__), 'asciimath/html')
1
+ require_relative 'asciimath/version'
2
+ require_relative 'asciimath/parser'
3
+ require_relative 'asciimath/mathml'
4
+ require_relative 'asciimath/html'
5
+ require_relative 'asciimath/latex'
@@ -0,0 +1,456 @@
1
+ module AsciiMath
2
+ module AST
3
+ def expression(*e)
4
+ case e.length
5
+ when 0
6
+ nil
7
+ when 1
8
+ e[0]
9
+ else
10
+ Sequence.new(e)
11
+ end
12
+ end
13
+
14
+ def paren(lparen, e, rparen)
15
+ Paren.new(lparen, e, rparen)
16
+ end
17
+
18
+ def group(lparen, e, rparen)
19
+ Group.new(lparen, e, rparen)
20
+ end
21
+
22
+ def subsup(e, sub, sup)
23
+ SubSup.new(e, sub, sup)
24
+ end
25
+
26
+ def sub(e, sub)
27
+ SubSup.new(e, sub, nil)
28
+ end
29
+
30
+ def sup(e, sup)
31
+ SubSup.new(e, nil, sup)
32
+ end
33
+
34
+ def unary(operator, e)
35
+ UnaryOp.new(operator, e)
36
+ end
37
+
38
+ def binary(operator, e1, e2)
39
+ BinaryOp.new(operator, e1, e2)
40
+ end
41
+
42
+ def infix(e1, operator, e2)
43
+ InfixOp.new(operator, e1, e2)
44
+ end
45
+
46
+ def text(value)
47
+ Text.new(value)
48
+ end
49
+
50
+ def number(value)
51
+ Number.new(value)
52
+ end
53
+
54
+ def symbol(symbol, text)
55
+ Symbol.new(symbol, text)
56
+ end
57
+
58
+ def identifier(value)
59
+ Identifier.new(value)
60
+ end
61
+
62
+ def matrix(lparen, rows, rparen)
63
+ Matrix.new(lparen, rows, rparen)
64
+ end
65
+
66
+ def color(r, g, b, text)
67
+ Color.new(r, g, b, text)
68
+ end
69
+
70
+ class Node
71
+ attr_reader :parent
72
+
73
+ def initialize
74
+ @parent = nil
75
+ end
76
+
77
+ protected
78
+
79
+ attr_writer :parent
80
+ end
81
+
82
+ class InnerNode < Node
83
+ include Enumerable
84
+
85
+ def initialize
86
+ super
87
+ @children = []
88
+ end
89
+
90
+ def [](*args)
91
+ @children[*args]
92
+ end
93
+
94
+ def length
95
+ @children.length
96
+ end
97
+
98
+ def each(&block)
99
+ @children.each(&block)
100
+ end
101
+
102
+ protected
103
+
104
+ def child_nodes
105
+ @children
106
+ end
107
+
108
+ def add(node)
109
+ node.parent.remove(node) if node.parent
110
+ node.parent = self
111
+ child_nodes << node
112
+ end
113
+
114
+ def remove(node)
115
+ node.parent = nil
116
+ child_nodes.delete(node)
117
+ end
118
+ end
119
+
120
+ class Sequence < InnerNode
121
+ def initialize(nodes)
122
+ super()
123
+ nodes.each { |node| add(node) }
124
+ end
125
+
126
+ def to_s
127
+ child_nodes.map { |node| node.to_s }.join(" ")
128
+ end
129
+
130
+ def ==(o)
131
+ o.class == self.class && o.child_nodes == child_nodes
132
+ end
133
+ end
134
+
135
+ class Paren < InnerNode
136
+ attr_reader :lparen
137
+ attr_reader :rparen
138
+
139
+ def initialize(lparen, e, rparen)
140
+ super()
141
+ @lparen = lparen
142
+ @rparen = rparen
143
+ add(e) if e
144
+ end
145
+
146
+ def expression
147
+ child_nodes[0]
148
+ end
149
+
150
+ def to_s
151
+ "#{lparen.nil? ? '' : lparen.text}#{expression}#{rparen.nil? ? '' : rparen.text}"
152
+ end
153
+
154
+ def ==(o)
155
+ o.class == self.class && o.lparen == lparen && o.expression == expression && o.rparen == rparen
156
+ end
157
+ end
158
+
159
+ class Group < InnerNode
160
+ attr_reader :lparen
161
+ attr_reader :rparen
162
+
163
+ def initialize(lparen, e, rparen)
164
+ super()
165
+ @lparen = lparen
166
+ @rparen = rparen
167
+ add(e) if e
168
+ end
169
+
170
+ def expression
171
+ child_nodes[0]
172
+ end
173
+
174
+ def to_s
175
+ "#{lparen.nil? ? '' : lparen.text}#{expression}#{rparen.nil? ? '' : rparen.text}"
176
+ end
177
+
178
+ def ==(o)
179
+ o.class == self.class && o.lparen == lparen && o.expression == expression && o.rparen == rparen
180
+ end
181
+ end
182
+
183
+ class SubSup < InnerNode
184
+ def initialize(e, sub, sup)
185
+ super()
186
+ add(e)
187
+ add(sub || Empty.new)
188
+ add(sup || Empty.new)
189
+ end
190
+
191
+ def base_expression
192
+ child_nodes[0]
193
+ end
194
+
195
+ def sub_expression
196
+ child = child_nodes[1]
197
+ child.is_a?(Empty) ? nil : child
198
+ end
199
+
200
+ def sup_expression
201
+ child = child_nodes[2]
202
+ child.is_a?(Empty) ? nil : child
203
+ end
204
+
205
+ def to_s
206
+ s = ""
207
+ s << base_expression.to_s
208
+ sub = sub_expression
209
+ if sub
210
+ s << "_" << sub.to_s
211
+ end
212
+ sup = sup_expression
213
+ if sup
214
+ s << "^" << sup.to_s
215
+ end
216
+ s
217
+ end
218
+
219
+ def ==(o)
220
+ o.class == self.class && o.base_expression == base_expression && o.sub_expression == sub_expression && o.sup_expression == sup_expression
221
+ end
222
+ end
223
+
224
+ class UnaryOp < InnerNode
225
+ def initialize(operator, e)
226
+ super()
227
+ add(operator)
228
+ add(e)
229
+ end
230
+
231
+ def operator
232
+ child_nodes[0]
233
+ end
234
+
235
+ def operand
236
+ child_nodes[1]
237
+ end
238
+
239
+ def to_s
240
+ "#{operator} #{operand}"
241
+ end
242
+
243
+ def ==(o)
244
+ o.class == self.class && o.operator == operator && o.operand == operand
245
+ end
246
+ end
247
+
248
+ class BinaryOp < InnerNode
249
+ def initialize(operator, e1, e2)
250
+ super()
251
+ add(operator)
252
+ add(e1)
253
+ add(e2)
254
+ end
255
+
256
+
257
+ def operator
258
+ child_nodes[0]
259
+ end
260
+
261
+ def operand1
262
+ child_nodes[1]
263
+ end
264
+
265
+ def operand2
266
+ child_nodes[2]
267
+ end
268
+
269
+ def to_s
270
+ "#{operator} #{operand1} #{operand2}"
271
+ end
272
+
273
+ def ==(o)
274
+ o.class == self.class && o.operator == operator && o.operand1 == operand1 && o.operand2 == operand2
275
+ end
276
+ end
277
+
278
+ class InfixOp < InnerNode
279
+ def initialize(operator, e1, e2)
280
+ super()
281
+ add(operator)
282
+ add(e1)
283
+ add(e2)
284
+ end
285
+
286
+
287
+ def operator
288
+ child_nodes[0]
289
+ end
290
+
291
+ def operand1
292
+ child_nodes[1]
293
+ end
294
+
295
+ def operand2
296
+ child_nodes[2]
297
+ end
298
+
299
+ def to_s
300
+ "#{operand1} #{operator} #{operand2}"
301
+ end
302
+
303
+ def ==(o)
304
+ o.class == self.class && o.operator == operator && o.operand1 == operand1 && o.operand2 == operand2
305
+ end
306
+ end
307
+
308
+ class ValueNode < Node
309
+ attr_reader :value
310
+
311
+ def initialize(value)
312
+ super()
313
+ @value = value
314
+ end
315
+
316
+ def to_s
317
+ value.to_s
318
+ end
319
+
320
+ def ==(o)
321
+ o.class == self.class && o.value == value
322
+ end
323
+ end
324
+
325
+ class Text < ValueNode
326
+ def initialize(value)
327
+ super(value.dup.freeze)
328
+ end
329
+
330
+ def to_s
331
+ '"' + super + '"'
332
+ end
333
+ end
334
+
335
+ class Number < ValueNode
336
+ def initialize(value)
337
+ super(value.dup.freeze)
338
+ end
339
+ end
340
+
341
+ class Symbol < ValueNode
342
+ attr_reader :text
343
+
344
+ def initialize(value, text)
345
+ super(value)
346
+ @text = text.dup.freeze
347
+ end
348
+
349
+ def ==(o)
350
+ super && o.text == text
351
+ end
352
+
353
+ def to_s
354
+ text
355
+ end
356
+ end
357
+
358
+ class Identifier < ValueNode
359
+ def initialize(value)
360
+ super(value.dup.freeze)
361
+ end
362
+ end
363
+
364
+ class Color < ValueNode
365
+ attr_reader :text
366
+
367
+ def initialize(r, g, b, text)
368
+ super({:r => r, :g => g, :b => b}.freeze)
369
+ @text = text.dup.freeze
370
+ end
371
+
372
+ def red
373
+ value[:r]
374
+ end
375
+
376
+ def green
377
+ value[:g]
378
+ end
379
+
380
+ def blue
381
+ value[:b]
382
+ end
383
+
384
+ def ==(o)
385
+ o.class == self.class &&
386
+ o.red == red &&
387
+ o.green == green &&
388
+ o.blue == blue &&
389
+ o.text == text
390
+ end
391
+
392
+ def to_hex_rgb
393
+ sprintf('#%02x%02x%02x', red, green, blue)
394
+ end
395
+
396
+ def to_s
397
+ text
398
+ end
399
+ end
400
+
401
+ class Matrix < InnerNode
402
+ attr_reader :lparen
403
+ attr_reader :rparen
404
+
405
+ def initialize(lparen, rows, rparen)
406
+ super()
407
+ @lparen = lparen
408
+ @rparen = rparen
409
+ rows.map { |row| MatrixRow.new(row) }.each { |row_seq| add(row_seq) }
410
+ end
411
+
412
+ def to_s
413
+ s = ""
414
+ s << (lparen.nil? ? '{:' : lparen.text)
415
+ s << child_nodes.map { |node| node.to_s }.join(",")
416
+ s << (rparen.nil? ? ':}' : rparen.text)
417
+ end
418
+
419
+ def ==(o)
420
+ o.class == self.class &&
421
+ o.lparen == lparen &&
422
+ o.child_nodes == child_nodes &&
423
+ o.rparen == rparen
424
+ end
425
+ end
426
+
427
+ class MatrixRow < InnerNode
428
+ def initialize(nodes)
429
+ super()
430
+ nodes.each { |node| add(node || Empty.new) }
431
+ end
432
+
433
+ def to_s
434
+ "(" + child_nodes.map { |node| node.to_s }.join(",") + ")"
435
+ end
436
+
437
+ def ==(o)
438
+ o.class == self.class && o.child_nodes == child_nodes
439
+ end
440
+ end
441
+
442
+ class Empty < Node
443
+ def initialize()
444
+ super
445
+ end
446
+
447
+ def to_s
448
+ ''
449
+ end
450
+
451
+ def ==(o)
452
+ o.class == self.class
453
+ end
454
+ end
455
+ end
456
+ end