mathml 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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