mathml 0.8.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.
data/README ADDED
@@ -0,0 +1,58 @@
1
+ = MathML Library
2
+ :lang:ja
3
+ 本ライブラリは、LaTeXの記法にしたがって記述された数式をMathMLに変換する処理を提供します。
4
+
5
+ :lang:en
6
+ This library provide converter from LaTeX mathematical expressions to MathML.
7
+
8
+ :lang:
9
+ = URLs
10
+ * Documents(Japanese)[http://mathml.rubyforge.org/ja/]
11
+ * Documents(Englise)[http://mathml.rubyforge.org/en/]
12
+ * MercurialRepository[https://hg.hinet.mydns.jp/math_ml/]
13
+
14
+ = Example
15
+ :lang:ja
16
+ 例えば MathML::String による Stringクラスの拡張を使えば、次のようにして変換処理を呼び出すことが出来ます。
17
+
18
+ :lang:en
19
+ For example, using extension of String class by MathML::String, you can use converter like bellow.
20
+
21
+ :lang:
22
+ #!/usr/bin/ruby
23
+ # require "rubygems"
24
+ require "math_ml/string"
25
+ puts 'x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}'.to_mathml
26
+
27
+ :lang:ja
28
+ このスクリプトの出力は次のようになります。
29
+
30
+ :lang:en
31
+ Output of this script is bellow.
32
+
33
+ :lang:
34
+ <math xmlns='http://www.w3.org/1998/Math/MathML' display='inline'>
35
+ <mi>x</mi>
36
+ <mo>=</mo>
37
+ <mfrac>
38
+ <mrow>
39
+ <mo>-</mo>
40
+ <mi>b</mi>
41
+ <mo>&pm;</mo>
42
+ <msqrt><mrow>
43
+ <msup>
44
+ <mi>b</mi>
45
+ <mn>2</mn>
46
+ </msup>
47
+ <mo>-</mo>
48
+ <mn>4</mn>
49
+ <mi>a</mi>
50
+ <mi>c</mi>
51
+ </mrow></msqrt>
52
+ </mrow>
53
+ <mrow>
54
+ <mn>2</mn>
55
+ <mi>a</mi>
56
+ </mrow>
57
+ </mfrac>
58
+ </math>
data/Rakefile ADDED
@@ -0,0 +1,121 @@
1
+ require "rake/clean"
2
+ require "rake/testtask"
3
+ require "rake/rdoctask"
4
+ require "rake/gempackagetask"
5
+
6
+ FILES = FileList["**/*"].exclude("pkg", "html", "eim_xml")
7
+ CLOBBER.include("eim_xml")
8
+
9
+ task :default => :test
10
+
11
+
12
+ ### Document ###
13
+ RDOC_DIR = "./html/"
14
+ RDOC_OPTS = ["-S", "-w", "3", "-c", "UTF-8", "-m", "README"]
15
+ RDOC_OPTS << "-d" if ENV["DOT"]
16
+ RDOC_FILES = FileList["lib/**/*.rb"]
17
+ RDOC_EXTRAS = FileList["README"]
18
+ ["", "ja", "en"].each do |l|
19
+ dir = RDOC_DIR.dup
20
+ dir << "#{l}/" unless l.empty?
21
+ Rake::RDocTask.new("rdoc#{":"+l unless l.empty?}") do |rdoc|
22
+ rdoc.title = "MathML Library"
23
+ rdoc.options = RDOC_OPTS.dup
24
+ rdoc.options << "-l" << l unless l.empty?
25
+ rdoc.rdoc_dir = dir
26
+ rdoc.rdoc_files.include(RDOC_FILES, RDOC_EXTRAS)
27
+ end
28
+ end
29
+ task "rdoc:all" => ["rdoc", "rdoc:ja", "rdoc:en"]
30
+ task "rerdoc:all" => ["rerdoc", "rerdoc:ja", "rerdoc:en"]
31
+
32
+
33
+ ### Publish document ###
34
+ task :publish => [:clobber_rdoc, "rdoc:ja", "rdoc:en"] do
35
+ require "rake/contrib/rubyforgepublisher"
36
+ cp "index.html", "html/index.html"
37
+ Rake::RubyForgePublisher.new("mathml", "hiraku").upload
38
+ end
39
+
40
+
41
+ ### Clone external library ###
42
+ EIMXML_DIR = "./eim_xml"
43
+ file EIMXML_DIR do |t|
44
+ sh "hg clone https://hg.hinet.mydns.jp/eim_xml #{t.name}"
45
+ end
46
+ task :eim_xml => EIMXML_DIR
47
+
48
+
49
+ ### Test ###
50
+ task :test => "test:apart"
51
+ namespace :test do
52
+ def add_libs_for_test(t)
53
+ t.libs << "test" << "eim_xml/lib" << "../eim_xml/lib"
54
+ end
55
+ FileList["test/*_test.rb"].sort{|a,b| File.mtime(a)<=>File.mtime(b)}.reverse.each do |i|
56
+ Rake::TestTask.new(:apart) do |t|
57
+ t.test_files = i
58
+ add_libs_for_test(t)
59
+ end
60
+ end
61
+ task(:apart).comment = "Run tests separately"
62
+
63
+ Rake::TestTask.new(:lump) do |t|
64
+ t.test_files = FileList["test/*_test.rb"]
65
+ add_libs_for_test(t)
66
+ end
67
+ task(:lump).comment = "Run all tests in a lump"
68
+
69
+ Rake::TestTask.new(:symbol) do |t|
70
+ t.test_files = FileList["symbols/*_test.rb"]
71
+ add_libs_for_test(t)
72
+ end
73
+ end
74
+
75
+
76
+ ### Build GEM ###
77
+ GEM_DIR = "./pkg"
78
+ directory GEM_DIR
79
+ def build_gem(unstable=false)
80
+ spec = Gem::Specification.new do |spec|
81
+ spec.name = "mathml"
82
+ spec.rubyforge_project = "mathml"
83
+ spec.version = "0.8.0"
84
+ spec.summary = "MathML Library"
85
+ spec.author = "KURODA Hiraku"
86
+ spec.email = "hiraku@hinet.mydns.jp"
87
+ spec.homepage = "http://mathml.rubyforge.org/"
88
+ spec.add_dependency("eimxml")
89
+ spec.files = FILES
90
+ spec.test_files = Dir.glob("test/*.rb")
91
+ spec.has_rdoc = true
92
+ spec.rdoc_options = RDOC_OPTS.dup
93
+ spec.extra_rdoc_files = RDOC_EXTRAS
94
+ end
95
+
96
+ spec.version = spec.version.to_s << Time.now.strftime(".%Y.%m%d.%H%M") if unstable
97
+ b = Gem::Builder.new(spec)
98
+ gname = b.build
99
+ mv gname, "#{GEM_DIR}/"
100
+ end
101
+
102
+ desc "Build gem package"
103
+ task :gem => GEM_DIR do
104
+ build_gem
105
+ end
106
+
107
+ desc "Build unstable version gem package"
108
+ task "gem:unstable" => GEM_DIR do
109
+ build_gem(true)
110
+ end
111
+
112
+
113
+ ### Build package ###
114
+ package_task = Rake::PackageTask.new("math_ml",ENV["VER"] || :noversion) do |t|
115
+ t.package_files.include(FILES)
116
+ t.need_tar_gz = true
117
+ end
118
+
119
+ file "#{package_task.package_dir_path}" => EIMXML_DIR do |t|
120
+ cp "#{EIMXML_DIR}/lib/eim_xml.rb", "#{t.name}/lib/"
121
+ end
data/lib/math_ml.rb ADDED
@@ -0,0 +1,1885 @@
1
+ # MathML Library
2
+ #
3
+ # Copyright (C) 2005, KURODA Hiraku <hiraku@hinet.mydns.jp>
4
+ # You can redistribute it and/or modify it under GPL2.
5
+
6
+ unless __FILE__ == File.expand_path(__FILE__)
7
+ require File.expand_path(File.dirname(__FILE__))+"/"+File.basename(__FILE__, ".rb")
8
+ else
9
+
10
+ require "strscan"
11
+ module MathML
12
+ require "eim_xml"
13
+
14
+ class XMLElement < EimXML::Element
15
+ def pop
16
+ @contents.pop
17
+ end
18
+ end
19
+
20
+ def self.pcstring(s, encoded=false)
21
+ EimXML::PCString.new(s, encoded)
22
+ end
23
+
24
+ PCString = EimXML::PCString
25
+
26
+ class Error < StandardError; end
27
+
28
+ class Element < XMLElement
29
+ attr_reader :display_style
30
+
31
+ def as_display_style
32
+ @display_style = true
33
+ self
34
+ end
35
+
36
+ def <<(s)
37
+ s = MathML.pcstring(s) if String===s
38
+ super(s)
39
+ end
40
+ end
41
+
42
+ module Variant
43
+ NORMAL = "normal"
44
+ BOLD = "bold"
45
+ BOLD_ITALIC = "bold-italic"
46
+ def variant=(v)
47
+ self["mathvariant"] = v
48
+ end
49
+ end
50
+
51
+ module Align
52
+ CENTER = "center"
53
+ LEFT = "left"
54
+ RIGHT = "right"
55
+ end
56
+
57
+ module Line
58
+ SOLID = "solid"
59
+ NONE = "none"
60
+ end
61
+
62
+ class Math < XMLElement
63
+ def initialize(display_style)
64
+ super("math", "xmlns"=>"http://www.w3.org/1998/Math/MathML")
65
+ self[:display] = display_style ? "block" : "inline"
66
+ end
67
+ end
68
+
69
+ class Row < Element
70
+ def initialize
71
+ super("mrow")
72
+ end
73
+ end
74
+
75
+ class None < Element
76
+ def initialize
77
+ super("none")
78
+ end
79
+ end
80
+
81
+ class Space < Element
82
+ def initialize(width)
83
+ super("mspace", "width"=>width)
84
+ end
85
+ end
86
+
87
+ class Fenced < Element
88
+ attr_reader :open, :close
89
+
90
+ def initialize
91
+ super("mfenced")
92
+ end
93
+
94
+ def open=(o)
95
+ o = "" if o.to_s=="." || !o
96
+ o = "{" if o.to_s=="\\{"
97
+ self[:open] = MathML.pcstring(o, true)
98
+ end
99
+
100
+ def close=(c)
101
+ c = "" if c.to_s=="." || !c
102
+ c = "}" if c.to_s=="\\}"
103
+ self[:close] = MathML.pcstring(c, true)
104
+ end
105
+ end
106
+
107
+ class Frac < Element
108
+ def initialize(numerator, denominator)
109
+ super("mfrac")
110
+ self << numerator
111
+ self << denominator
112
+ end
113
+ end
114
+
115
+ class SubSup < Element
116
+ attr_reader :sub, :sup, :body
117
+
118
+ def initialize(display_style, body)
119
+ super("mrow")
120
+ as_display_style if display_style
121
+ @body = body
122
+
123
+ update_name
124
+ end
125
+
126
+ def update_name
127
+ if @sub || @sup
128
+ name = "m"
129
+ name << (@sub ? (@display_style ? "under" : "sub") : "")
130
+ name << (@sup ? (@display_style ? "over" : "sup") : "")
131
+ else
132
+ name = "mrow"
133
+ end
134
+ self.name = name
135
+ end
136
+ protected :update_name
137
+
138
+ def update_contents
139
+ contents.clear
140
+ contents << @body
141
+ contents << @sub if @sub
142
+ contents << @sup if @sup
143
+ end
144
+ protected :update_contents
145
+
146
+ def sub=(sub)
147
+ @sub = sub
148
+ update_name
149
+ end
150
+
151
+ def sup=(sup)
152
+ @sup = sup
153
+ update_name
154
+ end
155
+
156
+ if Module.constants.include?("REXML") && self < REXML::Element
157
+ def write(writer=$stdout, indent=-1, transitive=false, ie_hack=true)
158
+ update_contents
159
+ super
160
+ end
161
+ elsif Module.constants.include?("EimXML") && self < EimXML::Element
162
+ def write(dst=String.new, level=0, is_head=true)
163
+ update_contents
164
+ super
165
+ end
166
+ end
167
+ end
168
+
169
+ class Over < Element
170
+ def initialize(base, over)
171
+ super("mover")
172
+ self << base << over
173
+ end
174
+ end
175
+
176
+ class Under < Element
177
+ def initialize(base, under)
178
+ super("munder")
179
+ self << base << under
180
+ end
181
+ end
182
+
183
+ class Number < Element
184
+ def initialize
185
+ super("mn")
186
+ end
187
+ end
188
+
189
+ class Identifier < Element
190
+ def initialize
191
+ super("mi")
192
+ end
193
+ end
194
+
195
+ class Operator < Element
196
+ def initialize
197
+ super("mo")
198
+ end
199
+ end
200
+
201
+ class Text < Element
202
+ def initialize
203
+ super("mtext")
204
+ end
205
+ end
206
+
207
+ class Sqrt < Element
208
+ def initialize
209
+ super("msqrt")
210
+ end
211
+ end
212
+
213
+ class Root < Element
214
+ def initialize(index, base)
215
+ super("mroot")
216
+ self << base
217
+ self << index
218
+ end
219
+ end
220
+
221
+ class Table < Element
222
+ def initialize
223
+ super("mtable")
224
+ end
225
+
226
+ def set_align_attribute(name, a, default)
227
+ if a.is_a?(Array) && a.size>0
228
+ value = ""
229
+ a.each do |i|
230
+ value << " "+i
231
+ end
232
+ if value =~ /^( #{default})*$/
233
+ @attributes.delete(name)
234
+ else
235
+ @attributes[name] = value.strip
236
+ end
237
+ else
238
+ @attributes.delete(name)
239
+ end
240
+ end
241
+
242
+ def aligns=(a)
243
+ set_align_attribute("columnalign", a, Align::CENTER)
244
+ end
245
+
246
+ def vlines=(a)
247
+ set_align_attribute("columnlines", a, Line::NONE)
248
+ end
249
+
250
+ def hlines=(a)
251
+ set_align_attribute("rowlines", a, Line::NONE)
252
+ end
253
+ end
254
+
255
+ class Tr < Element
256
+ def initialize
257
+ super("mtr")
258
+ end
259
+ end
260
+
261
+ class Td < Element
262
+ def initialize
263
+ super("mtd")
264
+ end
265
+ end
266
+
267
+ module LaTeX
268
+ MBEC = /\\.|[^\\]/m
269
+
270
+ module RE
271
+ SPACE = /(?:\s|%.*$)/
272
+ NUMERICS = /(?:\.\d+)|(?:\d+(\.\d+)?)/
273
+ OPERATORS = /[,\.\+\-\*=\/\(\)\[\]<>"'|;:!]/
274
+ ALPHABETS = /[a-zA-Z]/
275
+ BLOCK = /\A\{(.*?)\}\z/m
276
+ OPTION = /\A\[(.*)\]\z/m
277
+ COMMANDS = /\\([a-zA-Z]+|[^a-zA-Z])/
278
+ WBSLASH = /\\\\/
279
+ BRACES = /\A([.|\[\]\(\)<>])\z/
280
+ end
281
+
282
+ module Font
283
+ NORMAL = 0
284
+ BOLD = 1
285
+ BLACKBOLD = 2
286
+ SCRIPT = 3
287
+ FRAKTUR = 4
288
+ ROMAN = 5
289
+ BOLD_ITALIC = 6
290
+ end
291
+
292
+ class BlockNotClosed < StandardError; end
293
+ class NotEnvironment < StandardError; end
294
+ class EnvironmentNotEnd < StandardError; end
295
+ class NeedParameter < StandardError; end
296
+ class EndMismatchToBegin < StandardError; end
297
+ class OptionNotClosed < StandardError; end
298
+
299
+ class Scanner < StringScanner
300
+ def done
301
+ self.string[0, pos]
302
+ end
303
+
304
+ def scan_space
305
+ _scan(/#{RE::SPACE}+/)
306
+ end
307
+
308
+ def skip_space_and(check_mode)
309
+ opos = pos
310
+ scan_space
311
+ r = yield
312
+ self.pos = opos if check_mode || !r
313
+ r
314
+ end
315
+
316
+ alias :_check :check
317
+ def check(re)
318
+ skip_space_and(true){_check(re)}
319
+ end
320
+
321
+ alias :_scan :scan
322
+ def scan(re)
323
+ skip_space_and(false){_scan(re)}
324
+ end
325
+
326
+ alias :_eos? :eos?
327
+ def eos?
328
+ _eos? || _check(/#{RE::SPACE}+\z/)
329
+ end
330
+
331
+ def check_command
332
+ check(RE::COMMANDS)
333
+ end
334
+
335
+ def scan_command
336
+ scan(RE::COMMANDS)
337
+ end
338
+
339
+ def peek_command
340
+ check_command ? self[1] : nil
341
+ end
342
+
343
+ def check_block
344
+ skip_space_and(true){scan_block}
345
+ end
346
+
347
+ def scan_block
348
+ return nil unless scan(/\{/)
349
+ block = "{"
350
+ bpos = pos-1
351
+ nest = 1
352
+ while _scan(/(#{MBEC}*?)([\{\}])/)
353
+ block << matched
354
+ case self[2]
355
+ when "{"
356
+ nest+=1
357
+ when "}"
358
+ nest-=1
359
+ break if nest==0
360
+ end
361
+ end
362
+ if nest>0
363
+ self.pos = bpos
364
+ raise BlockNotClosed
365
+ end
366
+ self.pos = bpos
367
+ _scan(/\A\{(#{Regexp.escape(block[RE::BLOCK, 1].to_s)})\}/)
368
+ end
369
+
370
+ def check_any(remain_space=false)
371
+ skip_space_and(true){scan_any(remain_space)}
372
+ end
373
+
374
+ def scan_any(remain_space=false)
375
+ p = pos
376
+ scan_space
377
+ r = remain_space ? matched.to_s : ""
378
+ case
379
+ when s = scan_block
380
+ when s = scan_command
381
+ else
382
+ unless _scan(/./) || remain_space
383
+ self.pos = p
384
+ return nil
385
+ end
386
+ s = matched.to_s
387
+ end
388
+ r << s
389
+ end
390
+
391
+ def scan_option
392
+ return nil unless scan(/\[/)
393
+ opt = "["
394
+ p = pos-1
395
+ until (s=scan_any(true)) =~ /\A#{RE::SPACE}*\]\z/
396
+ opt << s
397
+ if eos?
398
+ self.pos = p
399
+ raise OptionNotClosed
400
+ end
401
+ end
402
+ opt << s
403
+ self.pos = p
404
+ _scan(/\A\[(#{Regexp.escape(opt[RE::OPTION, 1].to_s)})\]/)
405
+ end
406
+
407
+ def check_option
408
+ skip_space_and(true){scan_option}
409
+ end
410
+ end
411
+
412
+ class ParseError < StandardError
413
+ attr_accessor :rest, :done
414
+ def initialize(message, rest = "", done = "")
415
+ @done = done
416
+ @rest = rest
417
+ super(message)
418
+ end
419
+
420
+ def inspect
421
+ "#{message} : '#{@done}' / '#{@rest}'\n"+backtrace[0..5].join("\n")
422
+ end
423
+ end
424
+
425
+ class Macro
426
+ class Command
427
+ attr_reader :num, :body, :option
428
+ def initialize(n, b, o)
429
+ @num = n
430
+ @body = b
431
+ @option = o
432
+ end
433
+ end
434
+
435
+ class Environment
436
+ attr_reader :num, :beginning, :ending, :option
437
+ def initialize(n, b, e, o)
438
+ @num = n
439
+ @beginning = b
440
+ @ending = e
441
+ @option = o
442
+ end
443
+ end
444
+
445
+ def initialize
446
+ @commands = Hash.new
447
+ @environments = Hash.new
448
+ end
449
+
450
+ def parse_error(message, rest="", whole=nil)
451
+ rest = whole[/\A.*?(#{Regexp.escape(rest)}.*\z)/, 1] if whole
452
+ rest << @scanner.rest
453
+ done = @scanner.string[0, @scanner.string.size-rest.size]
454
+ ParseError.new(message, rest, done)
455
+ end
456
+
457
+ def parse(src)
458
+ @scanner = Scanner.new(src)
459
+ until @scanner.eos?
460
+ unless @scanner.scan_command
461
+ @scanner.scan_space
462
+ raise parse_error("Syntax error.")
463
+ end
464
+ case @scanner[1]
465
+ when "newcommand"
466
+ parse_newcommand
467
+ when "newenvironment"
468
+ parse_newenvironment
469
+ else
470
+ raise parse_error("Syntax error.", @scanner.matched)
471
+ end
472
+ end
473
+ rescue BlockNotClosed => e
474
+ raise parse_error("Block not closed.")
475
+ rescue OptionNotClosed => e
476
+ raise parse_error("Option not closed.")
477
+ end
478
+
479
+ def scan_num_of_parameter
480
+ if @scanner.scan_option
481
+ raise parse_error("Need positive number.", @scanner[1]+"]") unless @scanner[1]=~/\A#{RE::SPACE}*\d+#{RE::SPACE}*\z/
482
+ @scanner[1].to_i
483
+ else
484
+ 0
485
+ end
486
+ end
487
+
488
+ def check_parameter_numbers(src, opt, whole)
489
+ s = Scanner.new(src)
490
+ until s.eos?
491
+ case
492
+ when s.scan(/#{MBEC}*?\#(\d+|.)/)
493
+ raise parse_error("Need positive number.") unless s[1]=~/\d+/
494
+ raise parse_error("Parameter \# too large.", s[1]+s.rest, whole) if s[1].to_i>opt
495
+ else
496
+ return nil
497
+ end
498
+ end
499
+ end
500
+
501
+ def parse_newcommand
502
+ case
503
+ when @scanner.scan_block
504
+ s = Scanner.new(@scanner[1])
505
+ raise parse_error("Need newcommand.", s.rest+"}") unless s.scan_command
506
+ com = s[1]
507
+ raise parse_error("Syntax error." ,s.rest+"}") unless s.eos?
508
+ when @scanner.scan_command
509
+ s = Scanner.new(@scanner[1])
510
+ com = s.scan_command
511
+ else
512
+ raise parse_error("Need newcommand.")
513
+ end
514
+
515
+ optnum = scan_num_of_parameter
516
+ opt = @scanner.scan_option ? @scanner[1] : nil
517
+
518
+ case
519
+ when @scanner.scan_block
520
+ body = @scanner[1]
521
+ when @scanner.scan_command
522
+ body = @scanner.matched
523
+ else
524
+ body = @scanner.scan(/./)
525
+ end
526
+
527
+ raise parse_error("Need parameter.") unless body
528
+
529
+ check_parameter_numbers(body, optnum, @scanner.matched)
530
+
531
+ optnum-=1 if opt
532
+ @commands[com] = Command.new(optnum, body, opt)
533
+ end
534
+
535
+ def parse_newenvironment
536
+ case
537
+ when @scanner.scan_block
538
+ env = @scanner[1]
539
+ when @scanner.scan_command
540
+ raise ParseError.new
541
+ when @scanner.scan(/./)
542
+ env = @scanner.matched
543
+ end
544
+ raise parse_error("Syntax error.", env[/\A.*?(\\.*\z)/, 1], @scanner.matched) if env=~/\\/
545
+
546
+ optnum = scan_num_of_parameter
547
+ opt = @scanner.scan_option ? @scanner[1] : nil
548
+
549
+ b = @scanner.scan_block ? @scanner[1] : @scanner.scan_any
550
+ raise parse_error("Need begin block.") unless b
551
+ check_parameter_numbers(b, optnum, @scanner.matched)
552
+ e = @scanner.scan_block ? @scanner[1] : @scanner.scan_any
553
+ raise parse_error("Need end block.") unless e
554
+ check_parameter_numbers(e, optnum, @scanner.matched)
555
+
556
+ optnum -= 1 if opt
557
+ @environments[env] = Environment.new(optnum, b, e, opt)
558
+ end
559
+
560
+ def commands(com)
561
+ @commands[com]
562
+ end
563
+
564
+ def expand_command(com, params, opt=nil)
565
+ return nil unless @commands.has_key?(com)
566
+ c = @commands[com]
567
+ opt = c.option if c.option && !opt
568
+ params.unshift(opt) if c.option
569
+ raise ParseError.new("Need more parameter.") if params.size < c.num
570
+
571
+ c.body.gsub(/(#{MBEC}*?)\#(\d+)/) do
572
+ $1.to_s << params[$2.to_i-1]
573
+ end
574
+ end
575
+
576
+ def environments(env)
577
+ @environments[env]
578
+ end
579
+
580
+ def expand_environment(env, body, params, opt=nil)
581
+ return nil unless @environments.has_key?(env)
582
+ e = @environments[env]
583
+ opt = e.option if e.option && !opt
584
+ params.unshift(opt) if e.option
585
+ raise ParseError.new("Need more parameter.") if params.size < e.num
586
+
587
+ bg = e.beginning.gsub(/(#{MBEC}*?)\#(\d+)/) do
588
+ $1.to_s << params[$2.to_i-1]
589
+ end
590
+
591
+ en = e.ending.gsub(/(#{MBEC}*?)\#(\d+)/) do
592
+ $1.to_s << params[$2.to_i-1]
593
+ end
594
+
595
+ " #{bg} #{body} #{en} "
596
+ end
597
+ end
598
+
599
+ module BuiltinCommands; end
600
+ module BuiltinGroups; end
601
+ module BuiltinEnvironments; end
602
+
603
+ class Parser
604
+ class CircularReferenceCommand < StandardError; end
605
+
606
+ include LaTeX
607
+
608
+ include BuiltinEnvironments
609
+ include BuiltinGroups
610
+ include BuiltinCommands
611
+
612
+ BUILTIN_MACRO = <<'EOS'
613
+ \newenvironment{smallmatrix}{\begin{matrix}}{\end{matrix}}
614
+ \newenvironment{pmatrix}{\left(\begin{matrix}}{\end{matrix}\right)}
615
+ \newenvironment{bmatrix}{\left[\begin{matrix}}{\end{matrix}\right]}
616
+ \newenvironment{Bmatrix}{\left\{\begin{matrix}}{\end{matrix}\right\}}
617
+ \newenvironment{vmatrix}{\left|\begin{matrix}}{\end{matrix}\right|}
618
+ \newenvironment{Vmatrix}{\left\|\begin{matrix}}{\end{matrix}\right\|}
619
+ EOS
620
+
621
+ attr_accessor :unsecure_entity
622
+ attr_reader :macro
623
+ def initialize
624
+ @unsecure_entity = false
625
+ @entities = Hash.new
626
+ @commands = Hash.new
627
+ @symbols = Hash.new
628
+ @delimiters = Array.new
629
+ @group_begins = Hash.new
630
+ @group_ends = Hash.new
631
+ @macro = Macro.new
632
+ @macro.parse(BUILTIN_MACRO)
633
+ @expanded_command = Array.new
634
+ @expanded_environment = Array.new
635
+
636
+ super
637
+ end
638
+
639
+ def add_entity(list)
640
+ list.each do |i|
641
+ @entities[i] = true
642
+ end
643
+ end
644
+
645
+ def parse(src, displaystyle=false)
646
+ @ds = displaystyle
647
+ begin
648
+ parse_into(src, Math.new(@ds), Font::NORMAL)
649
+ rescue ParseError => e
650
+ e.done = src[0...(src.size - e.rest.size)]
651
+ raise
652
+ end
653
+ end
654
+
655
+ def push_container(container, scanner=@scanner, font=@font)
656
+ data = [@container, @scanner, @font]
657
+ @container, @scanner, @font = [container, scanner, font]
658
+ begin
659
+ yield container
660
+ container
661
+ ensure
662
+ @container, @scanner, @font = data
663
+ end
664
+ end
665
+
666
+ def add_plugin(plugin)
667
+ self.extend(plugin)
668
+ end
669
+
670
+ def add_commands(*a)
671
+ if a.size==1 && Hash===a[0]
672
+ @commands.merge!(a[0])
673
+ else
674
+ a.each{|i| @commands[i] = false}
675
+ end
676
+ end
677
+
678
+ def add_multi_command(m, *a)
679
+ a.each{|i| @commands[i] = m}
680
+ end
681
+
682
+ def add_sym_cmd(hash)
683
+ @symbols.merge!(hash)
684
+ end
685
+
686
+ def add_delimiter(list)
687
+ @delimiters.concat(list)
688
+ end
689
+
690
+ def add_group(begin_name, end_name, method=nil)
691
+ @group_begins[begin_name] = method
692
+ @group_ends[end_name] = begin_name
693
+ end
694
+
695
+ private
696
+ def parse_into(src, parent, font=nil)
697
+ orig = [@scanner, @container, @font, @ds]
698
+ @scanner = Scanner.new(src)
699
+ @container = parent
700
+ @font = font if font
701
+ begin
702
+ until @scanner.eos?
703
+ @container << parse_to_element(true)
704
+ end
705
+ @container
706
+ rescue BlockNotClosed => e
707
+ raise ParseError.new("Block not closed.", @scanner.rest)
708
+ rescue NotEnvironment => e
709
+ raise ParseError.new("Not environment.", @scanner.rest)
710
+ rescue EnvironmentNotEnd => e
711
+ raise ParseError.new("Environment not end.", @scanner.rest)
712
+ rescue OptionNotClosed => e
713
+ raise ParseError.new("Option not closed.", @scanner.rest)
714
+ rescue ParseError => e
715
+ e.rest << @scanner.rest.to_s
716
+ raise
717
+ ensure
718
+ @scanner, @container, @font, @ds = orig
719
+ end
720
+ end
721
+
722
+ def parse_any(message = "Syntax error.")
723
+ raise ParseError.new(message) unless @scanner.scan_any
724
+ s = @scanner
725
+ @scanner = Scanner.new(@scanner.matched)
726
+ begin
727
+ parse_to_element
728
+ ensure
729
+ @scanner = s
730
+ end
731
+ end
732
+
733
+ def parse_to_element(whole_group = false)
734
+ if whole_group && @group_begins.has_key?(@scanner.peek_command)
735
+ @scanner.scan_command
736
+ parse_group
737
+ else
738
+ case
739
+ when @scanner.scan(RE::NUMERICS)
740
+ parse_num
741
+ when @scanner.scan(RE::ALPHABETS)
742
+ parse_char
743
+ when @scanner.scan(RE::OPERATORS)
744
+ parse_operator
745
+ when @scanner.scan_block
746
+ parse_block
747
+ when @scanner.scan(/_/)
748
+ parse_sub
749
+ when @scanner.scan(/\^/)
750
+ parse_sup
751
+ when @scanner.scan_command
752
+ parse_command
753
+ else
754
+ raise ParseError.new('Syntax error.')
755
+ end
756
+ end
757
+ end
758
+
759
+ def parse_num
760
+ n = Number.new
761
+ n.extend(Variant).variant = Variant::BOLD if @font==Font::BOLD
762
+ n << @scanner.matched
763
+ end
764
+
765
+ def parse_char
766
+ c = @scanner.matched
767
+ i = Identifier.new
768
+ case @font
769
+ when Font::ROMAN
770
+ i.extend(Variant).variant = Variant::NORMAL
771
+ when Font::BOLD
772
+ i.extend(Variant).variant = Variant::BOLD
773
+ when Font::BOLD_ITALIC
774
+ i.extend(Variant).variant = Variant::BOLD_ITALIC
775
+ when Font::BLACKBOLD
776
+ c = MathML.pcstring(%Q[&#{c}opf;], true)
777
+ when Font::SCRIPT
778
+ c = MathML.pcstring(%Q[&#{c}scr;], true)
779
+ when Font::FRAKTUR
780
+ c = MathML.pcstring(%Q[&#{c}fr;], true)
781
+ end
782
+ i << c
783
+ end
784
+
785
+ def parse_operator
786
+ o = @scanner.matched
787
+ Operator.new << o
788
+ end
789
+
790
+ def parse_block
791
+ os = @scanner
792
+ @scanner = Scanner.new(@scanner[1])
793
+ begin
794
+ push_container(Row.new) do |r|
795
+ r << parse_to_element(true) until @scanner.eos?
796
+ end
797
+ rescue ParseError => e
798
+ e.rest << '}'
799
+ raise
800
+ ensure
801
+ @scanner = os
802
+ end
803
+ end
804
+
805
+ def parse_sub
806
+ e = @container.pop
807
+ e = None.new unless e
808
+ e = SubSup.new(@ds && e.display_style, e) unless e.is_a?(SubSup)
809
+ raise ParseError.new("Double subscript.", "_") if e.sub
810
+ e.sub = parse_any("Subscript not exist.")
811
+ e
812
+ end
813
+
814
+ def parse_sup
815
+ e = @container.pop
816
+ e = None.new unless e
817
+ e = SubSup.new(@ds && e.display_style, e) unless e.is_a?(SubSup)
818
+ raise ParseError.new("Double superscript.", "^") if e.sup
819
+ e.sup = parse_any("Superscript not exist.")
820
+ e
821
+ end
822
+
823
+ def entitize(str)
824
+ MathML.pcstring(str.sub(/^(.*)$/){"&#{$1};"}, true)
825
+ end
826
+
827
+ def parse_symbol_command(com, plain=false)
828
+ unless @symbols.include?(com)
829
+ @scanner.pos = @scanner.pos-(com.size+1)
830
+ raise ParseError.new("Undefined command.")
831
+ end
832
+ data = @symbols[com]
833
+ return nil unless data
834
+
835
+ su = data[0]
836
+ el = data[1]
837
+ el = :o unless el
838
+ s = data[2]
839
+ s = com.dup.untaint.to_sym unless s
840
+ s = com if s.is_a?(String) && s.length==0
841
+
842
+ case el
843
+ when :I
844
+ el = Identifier.new
845
+ when :i
846
+ el = Identifier.new
847
+ el.extend(Variant).variant = Variant::NORMAL unless s.is_a?(String)&&s.length>1
848
+ when :o
849
+ el = Operator.new
850
+ when :n
851
+ el = Number.new
852
+ else
853
+ raise ParseError.new("Inner data broken.")
854
+ end
855
+
856
+ case s
857
+ when String
858
+ when Fixnum
859
+ s = "&\#x#{s.to_s(16)};"
860
+ when Symbol
861
+ s = "&#{s.to_s};"
862
+ end
863
+
864
+ return s if plain
865
+ el << MathML.pcstring(s, true)
866
+ el.as_display_style if su==:u
867
+ el
868
+ end
869
+
870
+ def parse_command
871
+ com = @scanner[1]
872
+ matched = @scanner.matched
873
+ pos = @scanner.pos-matched.size
874
+ macro = @macro.commands(com)
875
+ if macro
876
+ begin
877
+ flg = @expanded_command.include?(com)
878
+ @expanded_command.push(com)
879
+ raise CircularReferenceCommand if flg
880
+ option = (macro.option && @scanner.scan_option) ? @scanner[1] : nil
881
+ params = Array.new
882
+ (1..macro.num).each do
883
+ params << (@scanner.scan_block ? @scanner[1] : @scanner.scan_any)
884
+ raise ParseError.new("Need more parameter.") unless params.last
885
+ end
886
+ r = parse_into(@macro.expand_command(com, params, option), Array.new)
887
+ return r
888
+ rescue CircularReferenceCommand
889
+ if @expanded_command.size>1
890
+ raise
891
+ else
892
+ @scanner.pos = pos
893
+ raise ParseError.new("Circular reference.")
894
+ end
895
+ rescue ParseError => e
896
+ if @expanded_command.size>1
897
+ raise
898
+ else
899
+ @scanner.pos = pos
900
+ raise ParseError.new(%[Error in macro(#{e.message} "#{e.rest.strip}").])
901
+ end
902
+ ensure
903
+ @expanded_command.pop
904
+ end
905
+ elsif @commands.key?(com)
906
+ m = @commands[com]
907
+ m = com unless m
908
+ return __send__("cmd_#{m.to_s}")
909
+ end
910
+ parse_symbol_command(com)
911
+ end
912
+
913
+ def parse_mathfont(font)
914
+ f = @font
915
+ @font = font
916
+ begin
917
+ push_container(Row.new){|r| r << parse_any}
918
+ ensure
919
+ @font = f
920
+ end
921
+ end
922
+
923
+ def parse_group
924
+ font = @font
925
+ begin
926
+ g = @group_begins[@scanner[1]]
927
+ g = @scanner[1] unless g
928
+ __send__("grp_#{g.to_s}")
929
+ ensure
930
+ @font = font
931
+ end
932
+ end
933
+ end
934
+
935
+ module BuiltinCommands
936
+ OVERS = {'hat'=>'circ', 'breve'=>'smile', 'grave'=>'grave',
937
+ 'acute'=>'acute', 'dot'=>'sdot', 'ddot'=>'nldr', 'tilde'=>'tilde',
938
+ 'bar'=>'macr', 'vec'=>'rightarrow', 'check'=>'vee', 'widehat'=>'circ',
939
+ 'overline'=>'macr', 'widetilde'=>'tilde', 'overbrace'=>'OverBrace'}
940
+ UNDERS = {'underbrace'=>'UnderBrace', 'underline'=>'macr'}
941
+
942
+ def initialize
943
+ add_commands("\\"=>:backslash)
944
+ add_commands("entity", "stackrel", "frac", "sqrt", "mbox")
945
+ add_multi_command(:hat_etc, 'hat', 'breve', 'grave', 'acute', 'dot', 'ddot', 'tilde', 'bar', 'vec', 'check', 'widehat', 'overline', 'widetilde', 'overbrace')
946
+ add_multi_command(:underbrace_etc, 'underbrace', 'underline')
947
+ add_multi_command(:quad_etc, "quad", "qquad", ",", ":", ";")
948
+ add_multi_command(:it_etc, "it", "rm", "bf")
949
+ add_multi_command(:mathit_etc, "mathit", "mathrm", "mathbf", "bm", "mathbb", "mathscr", "mathfrak")
950
+ add_sym_cmd(SymbolCommands)
951
+ add_delimiter(Delimiters)
952
+
953
+ super
954
+ end
955
+
956
+ def cmd_backslash
957
+ @ds ? nil : XMLElement.new("br", "xmlns"=>"http://www.w3.org/1999/xhtml")
958
+ end
959
+
960
+ def cmd_hat_etc
961
+ com = @scanner[1]
962
+ Over.new(parse_any, Operator.new << entitize(OVERS[com]))
963
+ end
964
+
965
+ def cmd_underbrace_etc
966
+ com = @scanner[1]
967
+ Under.new(parse_any, Operator.new << entitize(UNDERS[com]))
968
+ end
969
+
970
+ def cmd_entity
971
+ param = @scanner.scan_block ? @scanner[1] : @scanner.scan(/./)
972
+ raise ParseError.new("Need parameter.") unless param
973
+ unless @unsecure_entity || @entities[param]
974
+ param =@scanner.matched[/\A\{#{RE::SPACE}*(.*\})\z/, 1] if @scanner.matched=~RE::BLOCK
975
+ @scanner.pos = @scanner.pos-(param.size)
976
+ raise ParseError.new("Unregistered entity.")
977
+ end
978
+ Operator.new << entitize(param)
979
+ end
980
+
981
+ def cmd_stackrel
982
+ o = parse_any; b = parse_any
983
+ Over.new(b, o)
984
+ end
985
+
986
+ def cmd_quad_etc
987
+ case @scanner[1]
988
+ when 'quad'
989
+ Space.new("1em")
990
+ when 'qquad'
991
+ Space.new("2em")
992
+ when ','
993
+ Space.new("0.167em")
994
+ when ':'
995
+ Space.new("0.222em")
996
+ when ';'
997
+ Space.new("0.278em")
998
+ end
999
+ end
1000
+
1001
+ def cmd_it_etc
1002
+ case @scanner[1]
1003
+ when 'it'
1004
+ @font = Font::NORMAL
1005
+ when 'rm'
1006
+ @font = Font::ROMAN
1007
+ when 'bf'
1008
+ @font = Font::BOLD
1009
+ end
1010
+ nil
1011
+ end
1012
+
1013
+ def cmd_mathit_etc
1014
+ case @scanner[1]
1015
+ when 'mathit'
1016
+ parse_mathfont(Font::NORMAL)
1017
+ when 'mathrm'
1018
+ parse_mathfont(Font::ROMAN)
1019
+ when 'mathbf'
1020
+ parse_mathfont(Font::BOLD)
1021
+ when 'bm'
1022
+ parse_mathfont(Font::BOLD_ITALIC)
1023
+ when 'mathbb'
1024
+ parse_mathfont(Font::BLACKBOLD)
1025
+ when 'mathscr'
1026
+ parse_mathfont(Font::SCRIPT)
1027
+ when 'mathfrak'
1028
+ parse_mathfont(Font::FRAKTUR)
1029
+ end
1030
+ end
1031
+
1032
+ def cmd_frac
1033
+ n = parse_any; d = parse_any
1034
+ Frac.new(n, d)
1035
+ end
1036
+
1037
+ def cmd_sqrt
1038
+ if @scanner.scan_option
1039
+ i = parse_into(@scanner[1], Array.new)
1040
+ i = i.size==1 ? i[0] : (Row.new << i)
1041
+ b = parse_any
1042
+ Root.new(i, b)
1043
+ else
1044
+ Sqrt.new << parse_any
1045
+ end
1046
+ end
1047
+
1048
+ def cmd_mbox
1049
+ @scanner.scan_any
1050
+ Text.new << (@scanner.matched =~ RE::BLOCK ? @scanner[1] : @scanner.matched)
1051
+ end
1052
+ end
1053
+
1054
+ module BuiltinGroups
1055
+ class CircularReferenceEnvironment < StandardError; end
1056
+
1057
+ def initialize
1058
+ add_group("begin", "end")
1059
+ add_group("left", "right", :left_etc)
1060
+ add_group("bigg", "bigg", :left_etc)
1061
+ @environments = Hash.new
1062
+
1063
+ super
1064
+ end
1065
+
1066
+ def add_environment(*a)
1067
+ @environments = Hash.new unless @environments
1068
+ if a.size==1 && Hash===a[0]
1069
+ @environments.merge!(hash)
1070
+ else
1071
+ a.each{|i| @environments[i] = false}
1072
+ end
1073
+ end
1074
+
1075
+ def grp_begin
1076
+ matched = @scanner.matched
1077
+ begin_pos = @scanner.pos-matched.size
1078
+ en = @scanner.scan_block ? @scanner[1] : @scanner.scan_any
1079
+ raise ParseError.new('Environment name not exist.') unless en
1080
+
1081
+ macro = @macro.environments(en)
1082
+ if macro
1083
+ begin
1084
+ flg = @expanded_environment.include?(en)
1085
+ @expanded_environment.push(en)
1086
+ raise CircularReferenceEnvironment if flg
1087
+
1088
+ pos = @scanner.pos
1089
+ option = (macro.option && @scanner.scan_option) ? @scanner[1] : nil
1090
+ params = Array.new
1091
+ (1..macro.num).each do
1092
+ params << (@scanner.scan_block ? @scanner[1] : @scanner.scan_any)
1093
+ raise ParseError.new("Need more parameter.") unless params.last
1094
+ end
1095
+ body = ""
1096
+ grpnest = 0
1097
+ until @scanner.peek_command=="end" && grpnest==0
1098
+ if @scanner.eos?
1099
+ @scanner.pos = pos
1100
+ raise ParseError.new('Matching \end not exist.')
1101
+ end
1102
+ com = @scanner.peek_command
1103
+ grpnest += 1 if @group_begins.has_key?(com)
1104
+ grpnest -=1 if @group_ends.has_key?(com) && @group_begins[com]
1105
+ raise ParseError.new("Syntax error.") if grpnest<0
1106
+
1107
+ body << @scanner.scan_any(true)
1108
+ end
1109
+ @scanner.scan_command
1110
+ raise ParseError.new("Environment mismatched.", @scanner.matched) unless en==(@scanner.scan_block ? @scanner[1] : @scanner.scan_any)
1111
+ begin
1112
+ return parse_into(@macro.expand_environment(en, body, params, option), Array.new)
1113
+ rescue CircularReferenceEnvironment
1114
+ if @expanded_environment.size>1
1115
+ raise
1116
+ else
1117
+ @scanner.pos = begin_pos
1118
+ raise ParseError.new("Circular reference.")
1119
+ end
1120
+ rescue ParseError => e
1121
+ if @expanded_environment.size>1
1122
+ raise
1123
+ else
1124
+ @scanner.pos = begin_pos
1125
+ raise ParseError.new(%[Error in macro(#{e.message} "#{e.rest.strip}").])
1126
+ end
1127
+ end
1128
+ ensure
1129
+ @expanded_environment.pop
1130
+ end
1131
+ end
1132
+
1133
+ raise ParseError.new("Undefined environment.") unless @environments.has_key?(en)
1134
+ e = @environments[en]
1135
+ e = en unless e # default method name
1136
+ __send__("env_#{e.to_s}")
1137
+ end
1138
+
1139
+ def grp_left_etc
1140
+ right =
1141
+ case @scanner[1]
1142
+ when "left"
1143
+ "right"
1144
+ when "bigg"
1145
+ "bigg"
1146
+ end
1147
+
1148
+ f = Fenced.new
1149
+ p = @scanner.pos
1150
+ o = @scanner.scan_any
1151
+ raise ParseError.new('Need brace here.') unless o && (o=~RE::BRACES || @delimiters.include?(o[RE::COMMANDS, 1]))
1152
+
1153
+ f.open = (o=~RE::BRACES ? o : parse_symbol_command(o[RE::COMMANDS, 1], true))
1154
+ f << push_container(Row.new) do |r|
1155
+ until @scanner.peek_command==right
1156
+ if @scanner.eos?
1157
+ @scanner.pos = p
1158
+ raise ParseError.new('Brace not closed.')
1159
+ end
1160
+ r << parse_to_element(true)
1161
+ end
1162
+ end
1163
+ @scanner.scan_command # skip right
1164
+ c = @scanner.scan_any
1165
+ raise ParseError.new('Need brace here.') unless c=~RE::BRACES || @delimiters.include?(c[RE::COMMANDS, 1])
1166
+ f.close = (c=~RE::BRACES ? c : parse_symbol_command(c[RE::COMMANDS, 1], true))
1167
+ f
1168
+ end
1169
+ end
1170
+
1171
+ module BuiltinEnvironments
1172
+ def initialize
1173
+ add_environment("array", "matrix")
1174
+
1175
+ super
1176
+ end
1177
+
1178
+ def env_array
1179
+ layout = @scanner.scan_block ? @scanner.matched : @scanner.scan(/./)
1180
+ l = Scanner.new(layout=~RE::BLOCK ? layout[RE::BLOCK, 1] : layout)
1181
+ t = Table.new
1182
+ aligns = Array.new
1183
+ vlines = Array.new
1184
+ vlined = l.check(/\|/)
1185
+ columned = false
1186
+ until l.eos?
1187
+ c = l.scan_any
1188
+ raise ParseError.new("Syntax error.", layout[/\A.*(#{Regexp.escape(c+l.rest)}.*\z)/m, 1]) unless c=~/[clr\|@]/
1189
+
1190
+ if c=='|'
1191
+ aligns << Align::CENTER if vlined
1192
+ vlines << Line::SOLID
1193
+ vlined = true
1194
+ columned = false
1195
+ else
1196
+ vlines << Line::NONE if columned
1197
+ vlined = false
1198
+ columned = true
1199
+ case c
1200
+ when 'l'
1201
+ aligns << Align::LEFT
1202
+ when 'c'
1203
+ aligns << Align::CENTER
1204
+ when 'r'
1205
+ aligns << Align::RIGHT
1206
+ when '@'
1207
+ aligns << Align::CENTER
1208
+ l.scan_any
1209
+ end
1210
+ end
1211
+ end
1212
+ t.aligns = aligns
1213
+ t.vlines = vlines
1214
+
1215
+ layout = layout[RE::BLOCK, 1] if layout=~RE::BLOCK
1216
+ raise ParseError.new('Need parameter here.') if layout==""
1217
+
1218
+ hlines = Array.new
1219
+ row_parsed = false
1220
+ hlined = false
1221
+ until @scanner.peek_command=="end"
1222
+ raise ParseError.new('Matching \end not exist.') if @scanner.eos?
1223
+ if @scanner.peek_command=="hline"
1224
+ @scanner.scan_command
1225
+ t << Tr.new unless row_parsed
1226
+ hlines << Line::SOLID
1227
+ row_parsed = false
1228
+ hlined = true
1229
+ else
1230
+ hlines << Line::NONE if row_parsed
1231
+ t << env_array_row(l.string)
1232
+ @scanner.scan(RE::WBSLASH)
1233
+ row_parsed = true
1234
+ hlined = false
1235
+ end
1236
+ end
1237
+ t.hlines = hlines
1238
+
1239
+ if hlined
1240
+ tr = Tr.new
1241
+ (0..vlines.size).each {|i| tr << Td.new}
1242
+ t << tr
1243
+ end
1244
+
1245
+ @scanner.scan_command
1246
+ raise ParseError.new("Environment mismatched.") unless @scanner.check_block && @scanner[1]=="array"
1247
+ @scanner.scan_block
1248
+ t
1249
+ end
1250
+
1251
+ def env_array_row(layout)
1252
+ l = Scanner.new(layout)
1253
+ r = Tr.new
1254
+ first_column = true
1255
+ vlined = l.check(/\|/)
1256
+ until l.eos?
1257
+ c = l.scan(/./)
1258
+ if c=='|'
1259
+ r << Td.new if vlined
1260
+ vlined = true
1261
+ next
1262
+ else
1263
+ vlined = false
1264
+ case c
1265
+ when 'r', 'l', 'c'
1266
+ when '@'
1267
+ r << parse_into(l.scan_any, Td.new)
1268
+ next
1269
+ end
1270
+ if first_column
1271
+ first_column = false
1272
+ else
1273
+ raise ParseError.new("Need more column.", @scanner.matched.to_s) unless @scanner.scan(/&/)
1274
+ end
1275
+ r << push_container(Td.new) do |td|
1276
+ td << parse_to_element(true) until @scanner.peek_command=="end" || @scanner.check(/(&|\\\\)/) || @scanner.eos?
1277
+ end
1278
+ end
1279
+ end
1280
+ r << Td.new if vlined
1281
+ raise ParseError.new("Too many column.") if @scanner.check(/&/)
1282
+ r
1283
+ end
1284
+
1285
+ def env_matrix
1286
+ t = Table.new
1287
+ hlines = Array.new
1288
+ hlined = false
1289
+ row_parsed = false
1290
+ until @scanner.peek_command=="end"
1291
+ raise ParseError.new('Matching \end not exist.') if @scanner.eos?
1292
+ if @scanner.peek_command=="hline"
1293
+ @scanner.scan_command
1294
+ t << Tr.new unless row_parsed
1295
+ hlines << Line::SOLID
1296
+ row_parsed = false
1297
+ hlined = true
1298
+ else
1299
+ hlines << Line::NONE if row_parsed
1300
+ t << (r = Tr.new)
1301
+ r << (td=Td.new)
1302
+ until @scanner.check(RE::WBSLASH) || @scanner.peek_command=="end" || @scanner.eos?
1303
+ push_container(td) do |e|
1304
+ e << parse_to_element(true) until @scanner.peek_command=="end" || @scanner.check(/(&|\\\\)/) || @scanner.eos?
1305
+ end
1306
+ r << (td=Td.new) if @scanner.scan(/&/)
1307
+ end
1308
+ @scanner.scan(RE::WBSLASH)
1309
+ row_parsed = true
1310
+ hlined = false
1311
+ end
1312
+ end
1313
+ t.hlines = hlines
1314
+
1315
+ t << Tr.new if hlined
1316
+
1317
+ raise ParseError.new("Need \\end{array}.") unless @scanner.peek_command=="end"
1318
+ @scanner.scan_command
1319
+ raise ParseError.new("Environment mismatched.") unless @scanner.check_block && @scanner[1]=="matrix"
1320
+ @scanner.scan_block
1321
+ t
1322
+ end
1323
+
1324
+ def env_matrix_row
1325
+ r = Tr.new
1326
+ until @scanner.check(RE::WBSLASH) || @scanner.peek_command=="end"
1327
+ r << push_container(Td.new) do |td|
1328
+ td << parse_to_element(true) until @scanner.peek_command=="end" || @scanner.check(/(&|\\\\)/) || @scanner.eos?
1329
+ end
1330
+ end
1331
+
1332
+ end
1333
+ end
1334
+ end
1335
+ end
1336
+
1337
+ # Automatically generated constants
1338
+ module MathML::LaTeX::BuiltinCommands
1339
+ SymbolCommands={
1340
+ "{"=>[:s,:o,""],
1341
+ "}"=>[:s,:o,""],
1342
+ "#"=>[:s,:o,""],
1343
+ "$"=>[:s,:o,""],
1344
+ "&"=>[:s,:o,:amp],
1345
+ "_"=>[:s,:o,""],
1346
+ "%"=>[:s,:o,""],
1347
+ ","=>nil,
1348
+ "varepsilon"=>[:s,:I],
1349
+ "mathdollar"=>[:s,:o,"$"],
1350
+ "lbrace"=>[:s],
1351
+ "rbrace"=>[:s],
1352
+ "P"=>[:s,:o,:para],
1353
+ "mathparagraph"=>[:s,:o,:para],
1354
+ "S"=>[:s,:o,:sect],
1355
+ "mathsection"=>[:s,:o,:sect],
1356
+ "dag"=>[:s,:o,:dagger],
1357
+ "dagger"=>[:s],
1358
+ "ddag"=>[:s,:o,:ddagger],
1359
+ "ddagger"=>[:s],
1360
+ "copyright"=>[:s,:o,:copy],
1361
+ "pounds"=>[:s,:o,:pound],
1362
+ "mathsterling"=>[:s,:o,:pound],
1363
+ "dots"=>[:s,:o,:mldr],
1364
+ "mathellipsis"=>[:s,:o,:mldr],
1365
+ "ldots"=>[:s,:o,:mldr],
1366
+ "ensuremath"=>nil,
1367
+ "|"=>[:s,:o,:DoubleVerticalBar],
1368
+ "mho"=>[:s],
1369
+ "Join"=>[:s,:o,:bowtie],
1370
+ "Box"=>[:s,:o,:square],
1371
+ "Diamond"=>[:s],
1372
+ "leadsto"=>[:s,:o,:zigrarr],
1373
+ "sqsubset"=>[:s],
1374
+ "sqsupset"=>[:s],
1375
+ "lhd"=>[:s,:o,:vltri],
1376
+ "unlhd"=>[:s,:o,:ltrie],
1377
+ "rhd"=>[:s,:o,:vrtri],
1378
+ "unrhd"=>[:s,:o,:rtrie],
1379
+ "log"=>[:s,:i,""],
1380
+ "lg"=>[:s,:i,""],
1381
+ "ln"=>[:s,:i,""],
1382
+ "lim"=>[:u,:i,""],
1383
+ "limsup"=>[:u,:i,"lim sup"],
1384
+ "liminf"=>[:u,:i,"lim inf"],
1385
+ "sin"=>[:s,:i,""],
1386
+ "arcsin"=>[:s,:i,""],
1387
+ "sinh"=>[:s,:i,""],
1388
+ "cos"=>[:s,:i,""],
1389
+ "arccos"=>[:s,:i,""],
1390
+ "cosh"=>[:s,:i,""],
1391
+ "tan"=>[:s,:i,""],
1392
+ "arctan"=>[:s,:i,""],
1393
+ "tanh"=>[:s,:i,""],
1394
+ "cot"=>[:s,:i,""],
1395
+ "coth"=>[:s,:i,""],
1396
+ "sec"=>[:s,:i,""],
1397
+ "csc"=>[:s,:i,""],
1398
+ "max"=>[:u,:i,""],
1399
+ "min"=>[:u,:i,""],
1400
+ "sup"=>[:u,:i,""],
1401
+ "inf"=>[:u,:i,""],
1402
+ "arg"=>[:s,:i,""],
1403
+ "ker"=>[:s,:i,""],
1404
+ "dim"=>[:s,:i,""],
1405
+ "hom"=>[:s,:i,""],
1406
+ "det"=>[:u,:i,""],
1407
+ "exp"=>[:s,:i,""],
1408
+ "Pr"=>[:u,:i,""],
1409
+ "gcd"=>[:u,:i,""],
1410
+ "deg"=>[:s,:i,""],
1411
+ "prime"=>[:s],
1412
+ "alpha"=>[:s,:I],
1413
+ "beta"=>[:s,:I],
1414
+ "gamma"=>[:s,:I],
1415
+ "delta"=>[:s,:I],
1416
+ "epsilon"=>[:s,:I],
1417
+ "zeta"=>[:s,:I],
1418
+ "eta"=>[:s,:I],
1419
+ "theta"=>[:s,:I],
1420
+ "iota"=>[:s,:I],
1421
+ "kappa"=>[:s,:I],
1422
+ "lambda"=>[:s,:I],
1423
+ "mu"=>[:s,:I],
1424
+ "nu"=>[:s,:I],
1425
+ "xi"=>[:s,:I],
1426
+ "pi"=>[:s,:I],
1427
+ "rho"=>[:s,:I],
1428
+ "sigma"=>[:s,:I],
1429
+ "tau"=>[:s,:I],
1430
+ "upsilon"=>[:s,:I],
1431
+ "phi"=>[:s,:I],
1432
+ "chi"=>[:s,:I],
1433
+ "psi"=>[:s,:I],
1434
+ "omega"=>[:s,:I],
1435
+ "vartheta"=>[:s,:I],
1436
+ "varpi"=>[:s,:I],
1437
+ "varrho"=>[:s,:I],
1438
+ "varsigma"=>[:s,:I],
1439
+ "varphi"=>[:s,:I],
1440
+ "Gamma"=>[:s,:i],
1441
+ "Delta"=>[:s,:i],
1442
+ "Theta"=>[:s,:i],
1443
+ "Lambda"=>[:s,:i],
1444
+ "Xi"=>[:s,:i],
1445
+ "Pi"=>[:s,:i],
1446
+ "Sigma"=>[:s,:i],
1447
+ "Upsilon"=>[:s,:i,:Upsi],
1448
+ "Phi"=>[:s,:i],
1449
+ "Psi"=>[:s,:i],
1450
+ "Omega"=>[:s,:i],
1451
+ "aleph"=>[:s,:i],
1452
+ "hbar"=>[:s,:i,:hslash],
1453
+ "imath"=>[:s,:i],
1454
+ "jmath"=>[:s,:i],
1455
+ "ell"=>[:s],
1456
+ "wp"=>[:s],
1457
+ "Re"=>[:s,:i],
1458
+ "Im"=>[:s,:i],
1459
+ "partial"=>[:s,:o,:part],
1460
+ "infty"=>[:s,:n,:infin],
1461
+ "emptyset"=>[:s,:i,:empty],
1462
+ "nabla"=>[:s,:i],
1463
+ "surd"=>[:s,:o,:Sqrt],
1464
+ "top"=>[:s],
1465
+ "bot"=>[:s],
1466
+ "angle"=>[:s],
1467
+ "not"=>[:s],
1468
+ "triangle"=>[:s],
1469
+ "forall"=>[:s],
1470
+ "exists"=>[:s,:o,:exist],
1471
+ "neg"=>[:s,:o,:not],
1472
+ "lnot"=>[:s,:o,:not],
1473
+ "flat"=>[:s],
1474
+ "natural"=>[:s],
1475
+ "sharp"=>[:s],
1476
+ "clubsuit"=>[:s],
1477
+ "diamondsuit"=>[:s],
1478
+ "heartsuit"=>[:s],
1479
+ "spadesuit"=>[:s],
1480
+ "coprod"=>[:u],
1481
+ "bigvee"=>[:u],
1482
+ "bigwedge"=>[:u],
1483
+ "biguplus"=>[:u],
1484
+ "bigcap"=>[:u],
1485
+ "bigcup"=>[:u],
1486
+ "intop"=>[:u,:o,:int],
1487
+ "int"=>[:s,:o],
1488
+ "prod"=>[:u],
1489
+ "sum"=>[:u],
1490
+ "bigotimes"=>[:u],
1491
+ "bigoplus"=>[:u],
1492
+ "bigodot"=>[:u],
1493
+ "ointop"=>[:u,:o,:oint],
1494
+ "oint"=>[:s],
1495
+ "bigsqcup"=>[:u],
1496
+ "smallint"=>[:u,:o,:int],
1497
+ "triangleleft"=>[:s],
1498
+ "triangleright"=>[:s],
1499
+ "bigtriangleup"=>[:s],
1500
+ "bigtriangledown"=>[:s],
1501
+ "wedge"=>[:s],
1502
+ "land"=>[:s,:o,:wedge],
1503
+ "vee"=>[:s],
1504
+ "lor"=>[:s,:o,:vee],
1505
+ "cap"=>[:s],
1506
+ "cup"=>[:s],
1507
+ "sqcap"=>[:s],
1508
+ "sqcup"=>[:s],
1509
+ "uplus"=>[:s],
1510
+ "amalg"=>[:s],
1511
+ "diamond"=>[:s],
1512
+ "bullet"=>[:s],
1513
+ "wr"=>[:s],
1514
+ "div"=>[:s],
1515
+ "odot"=>[:s],
1516
+ "oslash"=>[:s],
1517
+ "otimes"=>[:s],
1518
+ "ominus"=>[:s],
1519
+ "oplus"=>[:s],
1520
+ "mp"=>[:s],
1521
+ "pm"=>[:s],
1522
+ "circ"=>[:s,:o,:cir],
1523
+ "bigcirc"=>[:s],
1524
+ "setminus"=>[:s],
1525
+ "cdot"=>[:s,:o,:sdot],
1526
+ "ast"=>[:s],
1527
+ "times"=>[:s],
1528
+ "star"=>[:s],
1529
+ "propto"=>[:s],
1530
+ "sqsubseteq"=>[:s],
1531
+ "sqsupseteq"=>[:s],
1532
+ "parallel"=>[:s],
1533
+ "mid"=>[:s],
1534
+ "dashv"=>[:s],
1535
+ "vdash"=>[:s],
1536
+ "nearrow"=>[:s],
1537
+ "searrow"=>[:s],
1538
+ "nwarrow"=>[:s],
1539
+ "swarrow"=>[:s],
1540
+ "Leftrightarrow"=>[:s],
1541
+ "Leftarrow"=>[:s],
1542
+ "Rightarrow"=>[:s],
1543
+ "neq"=>[:s,:o,:ne],
1544
+ "ne"=>[:s],
1545
+ "leq"=>[:s],
1546
+ "le"=>[:s],
1547
+ "geq"=>[:s],
1548
+ "ge"=>[:s],
1549
+ "succ"=>[:s],
1550
+ "prec"=>[:s],
1551
+ "approx"=>[:s],
1552
+ "succeq"=>[:s,:o,:sccue],
1553
+ "preceq"=>[:s,:o,:prcue],
1554
+ "supset"=>[:s],
1555
+ "subset"=>[:s],
1556
+ "supseteq"=>[:s],
1557
+ "subseteq"=>[:s],
1558
+ "in"=>[:s],
1559
+ "ni"=>[:s],
1560
+ "owns"=>[:s,:o,:ni],
1561
+ "gg"=>[:s],
1562
+ "ll"=>[:s],
1563
+ "leftrightarrow"=>[:s],
1564
+ "leftarrow"=>[:s],
1565
+ "gets"=>[:s,:o,:leftarrow],
1566
+ "rightarrow"=>[:s],
1567
+ "to"=>[:s,:o,:rightarrow],
1568
+ "mapstochar"=>[:s,:o,:vdash],
1569
+ "mapsto"=>[:s],
1570
+ "sim"=>[:s],
1571
+ "simeq"=>[:s],
1572
+ "perp"=>[:s],
1573
+ "equiv"=>[:s],
1574
+ "asymp"=>[:s],
1575
+ "smile"=>[:s],
1576
+ "frown"=>[:s],
1577
+ "leftharpoonup"=>[:s],
1578
+ "leftharpoondown"=>[:s],
1579
+ "rightharpoonup"=>[:s],
1580
+ "rightharpoondown"=>[:s],
1581
+ "cong"=>[:s],
1582
+ "notin"=>[:s],
1583
+ "rightleftharpoons"=>[:s],
1584
+ "doteq"=>[:s],
1585
+ "joinrel"=>nil,
1586
+ "relbar"=>[:s,:o,"-"],
1587
+ "Relbar"=>[:s,:o,"="],
1588
+ "lhook"=>[:s,:o,:sub],
1589
+ "hookrightarrow"=>[:s],
1590
+ "rhook"=>[:s,:o,:sup],
1591
+ "hookleftarrow"=>[:s],
1592
+ "bowtie"=>[:s],
1593
+ "models"=>[:s],
1594
+ "Longrightarrow"=>[:s],
1595
+ "longrightarrow"=>[:s],
1596
+ "longleftarrow"=>[:s],
1597
+ "Longleftarrow"=>[:s],
1598
+ "longmapsto"=>[:s,:o,:mapsto],
1599
+ "longleftrightarrow"=>[:s],
1600
+ "Longleftrightarrow"=>[:s],
1601
+ "iff"=>[:s],
1602
+ "ldotp"=>[:s,:o,"."],
1603
+ "cdotp"=>[:s,:o,:cdot],
1604
+ "colon"=>[:s],
1605
+ "cdots"=>[:s,:o,:ctdot],
1606
+ "vdots"=>[:s,:o,:vellip],
1607
+ "ddots"=>[:s,:o,:dtdot],
1608
+ "braceld"=>[:s,:o,0x25dc],
1609
+ "bracerd"=>[:s,:o,0x25dd],
1610
+ "bracelu"=>[:s,:o,0x25df],
1611
+ "braceru"=>[:s,:o,0x25de],
1612
+ "lmoustache"=>[:s],
1613
+ "rmoustache"=>[:s],
1614
+ "arrowvert"=>[:s,:o,:vert],
1615
+ "Arrowvert"=>[:s,:o,:DoubleVerticalBar],
1616
+ "Vert"=>[:s,:o,:DoubleVerticalBar],
1617
+ "vert"=>[:s],
1618
+ "uparrow"=>[:s],
1619
+ "downarrow"=>[:s],
1620
+ "updownarrow"=>[:s],
1621
+ "Uparrow"=>[:s],
1622
+ "Downarrow"=>[:s],
1623
+ "Updownarrow"=>[:s],
1624
+ "backslash"=>[:s,:o,"\\"],
1625
+ "rangle"=>[:s],
1626
+ "langle"=>[:s],
1627
+ "rceil"=>[:s],
1628
+ "lceil"=>[:s],
1629
+ "rfloor"=>[:s],
1630
+ "lfloor"=>[:s],
1631
+ "lgroup"=>[:s,:o,0x2570],
1632
+ "rgroup"=>[:s,:o,0x256f],
1633
+ "bracevert"=>[:s,:o,:vert],
1634
+ "mathunderscore"=>[:s,:o,"_"],
1635
+ "square"=>[:s],
1636
+ "rightsquigarrow"=>[:s],
1637
+ "lozenge"=>[:s],
1638
+ "vartriangleright"=>[:s],
1639
+ "vartriangleleft"=>[:s],
1640
+ "trianglerighteq"=>[:s],
1641
+ "trianglelefteq"=>[:s],
1642
+ "boxdot"=>[:s,:o,:dotsquare],
1643
+ "boxplus"=>[:s],
1644
+ "boxtimes"=>[:s],
1645
+ "blacksquare"=>[:s],
1646
+ "centerdot"=>[:s],
1647
+ "blacklozenge"=>[:s],
1648
+ "circlearrowright"=>[:s],
1649
+ "circlearrowleft"=>[:s],
1650
+ "leftrightharpoons"=>[:s],
1651
+ "boxminus"=>[:s],
1652
+ "Vdash"=>[:s],
1653
+ "Vvdash"=>[:s],
1654
+ "vDash"=>[:s],
1655
+ "twoheadrightarrow"=>[:s],
1656
+ "twoheadleftarrow"=>[:s],
1657
+ "leftleftarrows"=>[:s],
1658
+ "rightrightarrows"=>[:s],
1659
+ "upuparrows"=>[:s],
1660
+ "downdownarrows"=>[:s],
1661
+ "upharpoonright"=>[:s],
1662
+ "restriction"=>[:s,:o,:upharpoonright],
1663
+ "downharpoonright"=>[:s],
1664
+ "upharpoonleft"=>[:s],
1665
+ "downharpoonleft"=>[:s],
1666
+ "rightarrowtail"=>[:s],
1667
+ "leftarrowtail"=>[:s],
1668
+ "leftrightarrows"=>[:s],
1669
+ "rightleftarrows"=>[:s],
1670
+ "Lsh"=>[:s],
1671
+ "Rsh"=>[:s],
1672
+ "leftrightsquigarrow"=>[:s],
1673
+ "looparrowleft"=>[:s],
1674
+ "looparrowright"=>[:s],
1675
+ "circeq"=>[:s],
1676
+ "succsim"=>[:s],
1677
+ "gtrsim"=>[:s],
1678
+ "gtrapprox"=>[:s],
1679
+ "multimap"=>[:s],
1680
+ "therefore"=>[:s],
1681
+ "because"=>[:s],
1682
+ "doteqdot"=>[:s],
1683
+ "Doteq"=>[:s,:o,:doteqdot],
1684
+ "triangleq"=>[:s],
1685
+ "precsim"=>[:s],
1686
+ "lesssim"=>[:s],
1687
+ "lessapprox"=>[:s],
1688
+ "eqslantless"=>[:s],
1689
+ "eqslantgtr"=>[:s],
1690
+ "curlyeqprec"=>[:s],
1691
+ "curlyeqsucc"=>[:s],
1692
+ "preccurlyeq"=>[:s],
1693
+ "leqq"=>[:s],
1694
+ "leqslant"=>[:s,:o,:leq],
1695
+ "lessgtr"=>[:s],
1696
+ "backprime"=>[:s],
1697
+ "risingdotseq"=>[:s],
1698
+ "fallingdotseq"=>[:s],
1699
+ "succcurlyeq"=>[:s],
1700
+ "geqq"=>[:s],
1701
+ "geqslant"=>[:s,:o,:geq],
1702
+ "gtrless"=>[:s],
1703
+ "bigstar"=>[:s],
1704
+ "between"=>[:s],
1705
+ "blacktriangledown"=>[:s],
1706
+ "blacktriangleright"=>[:s],
1707
+ "blacktriangleleft"=>[:s],
1708
+ "vartriangle"=>[:s,:o,:triangle],
1709
+ "blacktriangle"=>[:s],
1710
+ "triangledown"=>[:s],
1711
+ "eqcirc"=>[:s],
1712
+ "lesseqgtr"=>[:s],
1713
+ "gtreqless"=>[:s],
1714
+ "lesseqqgtr"=>[:s],
1715
+ "gtreqqless"=>[:s],
1716
+ "Rrightarrow"=>[:s],
1717
+ "Lleftarrow"=>[:s],
1718
+ "veebar"=>[:s],
1719
+ "barwedge"=>[:s],
1720
+ "doublebarwedge"=>[:s],
1721
+ "measuredangle"=>[:s],
1722
+ "sphericalangle"=>[:s,:o,:angsph],
1723
+ "varpropto"=>[:s],
1724
+ "smallsmile"=>[:s,:o,:smile],
1725
+ "smallfrown"=>[:s,:o,:frown],
1726
+ "Subset"=>[:s],
1727
+ "Supset"=>[:s],
1728
+ "Cup"=>[:s],
1729
+ "doublecup"=>[:s,:o,:Cup],
1730
+ "Cap"=>[:s],
1731
+ "doublecap"=>[:s,:o,:Cap],
1732
+ "curlywedge"=>[:s],
1733
+ "curlyvee"=>[:s],
1734
+ "leftthreetimes"=>[:s],
1735
+ "rightthreetimes"=>[:s],
1736
+ "subseteqq"=>[:s],
1737
+ "supseteqq"=>[:s],
1738
+ "bumpeq"=>[:s],
1739
+ "Bumpeq"=>[:s],
1740
+ "lll"=>[:s,:o,:Ll],
1741
+ "llless"=>[:s,:o,:Ll],
1742
+ "ggg"=>[:s],
1743
+ "gggtr"=>[:s,:o,:ggg],
1744
+ "circledS"=>[:s],
1745
+ "pitchfork"=>[:s],
1746
+ "dotplus"=>[:s],
1747
+ "backsim"=>[:s],
1748
+ "backsimeq"=>[:s],
1749
+ "complement"=>[:s],
1750
+ "intercal"=>[:s],
1751
+ "circledcirc"=>[:s],
1752
+ "circledast"=>[:s],
1753
+ "circleddash"=>[:s],
1754
+ "lvertneqq"=>[:s,:o,:lneqq],
1755
+ "gvertneqq"=>[:s,:o,:gneqq],
1756
+ "nleq"=>[:s,:o,0x2270],
1757
+ "ngeq"=>[:s,:o,0x2271],
1758
+ "nless"=>[:s],
1759
+ "ngtr"=>[:s],
1760
+ "nprec"=>[:s],
1761
+ "nsucc"=>[:s],
1762
+ "lneqq"=>[:s],
1763
+ "gneqq"=>[:s],
1764
+ "nleqslant"=>[:s],
1765
+ "ngeqslant"=>[:s],
1766
+ "lneq"=>[:s],
1767
+ "gneq"=>[:s],
1768
+ "npreceq"=>[:s,:o,:nprcue],
1769
+ "nsucceq"=>[:s,:o,:nsccue],
1770
+ "precnsim"=>[:s],
1771
+ "succnsim"=>[:s],
1772
+ "lnsim"=>[:s],
1773
+ "gnsim"=>[:s],
1774
+ "nleqq"=>[:s],
1775
+ "ngeqq"=>[:s],
1776
+ "precneqq"=>[:s,:o,"<mfrac linethickness='0' mathsize='1%'><mo>&prec;</mo><mo>&ne;</mo></mfrac>"],
1777
+ "succneqq"=>[:s,:o,"<mfrac linethickness='0' mathsize='1%'><mo>&succ;</mo><mo>&ne;</mo></mfrac>"],
1778
+ "precnapprox"=>[:s],
1779
+ "succnapprox"=>[:s],
1780
+ "lnapprox"=>[:s,:o,"<mfrac linethickness='0' mathsize='1%'><mo>&lt;</mo><mo>&nap;</mo></mfrac>"],
1781
+ "gnapprox"=>[:s,:o,"<mfrac linethickness='0' mathsize='1%'><mo>&gt;</mo><mo>&nap;</mo></mfrac>"],
1782
+ "nsim"=>[:s],
1783
+ "ncong"=>[:s],
1784
+ "diagup"=>[:s,:o,0x2571],
1785
+ "diagdown"=>[:s,:o,0x2572],
1786
+ "varsubsetneq"=>[:s,:o,:subsetneq],
1787
+ "varsupsetneq"=>[:s,:o,:supsetneq],
1788
+ "nsubseteqq"=>[:s],
1789
+ "nsupseteqq"=>[:s],
1790
+ "subsetneqq"=>[:s],
1791
+ "supsetneqq"=>[:s],
1792
+ "varsubsetneqq"=>[:s,:o,:subsetneqq],
1793
+ "varsupsetneqq"=>[:s,:o,:supsetneqq],
1794
+ "subsetneq"=>[:s],
1795
+ "supsetneq"=>[:s],
1796
+ "nsubseteq"=>[:s],
1797
+ "nsupseteq"=>[:s],
1798
+ "nparallel"=>[:s],
1799
+ "nmid"=>[:s],
1800
+ "nshortmid"=>[:s,:o,:nmid],
1801
+ "nshortparallel"=>[:s,:o,:nparallel],
1802
+ "nvdash"=>[:s],
1803
+ "nVdash"=>[:s],
1804
+ "nvDash"=>[:s],
1805
+ "nVDash"=>[:s],
1806
+ "ntrianglerighteq"=>[:s],
1807
+ "ntrianglelefteq"=>[:s],
1808
+ "ntriangleleft"=>[:s],
1809
+ "ntriangleright"=>[:s],
1810
+ "nleftarrow"=>[:s],
1811
+ "nrightarrow"=>[:s],
1812
+ "nLeftarrow"=>[:s],
1813
+ "nRightarrow"=>[:s],
1814
+ "nLeftrightarrow"=>[:s],
1815
+ "nleftrightarrow"=>[:s],
1816
+ "divideontimes"=>[:s],
1817
+ "varnothing"=>[:s],
1818
+ "nexists"=>[:s],
1819
+ "Finv"=>[:s,:o,0x2132],
1820
+ "Game"=>[:s,:o,"G"],
1821
+ "eth"=>[:s],
1822
+ "eqsim"=>[:s],
1823
+ "beth"=>[:s],
1824
+ "gimel"=>[:s],
1825
+ "daleth"=>[:s],
1826
+ "lessdot"=>[:s],
1827
+ "gtrdot"=>[:s],
1828
+ "ltimes"=>[:s],
1829
+ "rtimes"=>[:s],
1830
+ "shortmid"=>[:s,:o,:mid],
1831
+ "shortparallel"=>[:s],
1832
+ "smallsetminus"=>[:s,:o,:setminus],
1833
+ "thicksim"=>[:s,:o,:sim],
1834
+ "thickapprox"=>[:s,:o,:approx],
1835
+ "approxeq"=>[:s],
1836
+ "succapprox"=>[:s],
1837
+ "precapprox"=>[:s],
1838
+ "curvearrowleft"=>[:s],
1839
+ "curvearrowright"=>[:s],
1840
+ "digamma"=>[:s],
1841
+ "varkappa"=>[:s],
1842
+ "Bbbk"=>[:s,:i,:kopf],
1843
+ "hslash"=>[:s],
1844
+ "backepsilon"=>[:s],
1845
+ "ulcorner"=>[:s,:o,:boxdr],
1846
+ "urcorner"=>[:s,:o,:boxdl],
1847
+ "llcorner"=>[:s,:o,:boxur],
1848
+ "lrcorner"=>[:s,:o,:boxul],
1849
+ }
1850
+ Delimiters=[
1851
+ "lmoustache",
1852
+ "rmoustache",
1853
+ "arrowvert",
1854
+ "Arrowvert",
1855
+ "Vert",
1856
+ "vert",
1857
+ "uparrow",
1858
+ "downarrow",
1859
+ "updownarrow",
1860
+ "Uparrow",
1861
+ "Downarrow",
1862
+ "Updownarrow",
1863
+ "backslash",
1864
+ "rangle",
1865
+ "langle",
1866
+ "rbrace",
1867
+ "lbrace",
1868
+ "rceil",
1869
+ "lceil",
1870
+ "rfloor",
1871
+ "lfloor",
1872
+ "lgroup",
1873
+ "rgroup",
1874
+ "bracevert",
1875
+ "ulcorner",
1876
+ "urcorner",
1877
+ "llcorner",
1878
+ "lrcorner",
1879
+ "{",
1880
+ "|",
1881
+ "}",
1882
+ ]
1883
+ end
1884
+
1885
+ end