| Class | MathML::LaTeX::Parser |
| In: |
lib/math_ml/latex.rb
|
| Parent: | Object |
| BUILTIN_MACRO | = | <<'EOS' \newenvironment{smallmatrix}{\begin{matrix}}{\end{matrix}} \newenvironment{pmatrix}{\left(\begin{matrix}}{\end{matrix}\right)} \newenvironment{bmatrix}{\left[\begin{matrix}}{\end{matrix}\right]} \newenvironment{Bmatrix}{\left\{\begin{matrix}}{\end{matrix}\right\}} \newenvironment{vmatrix}{\left|\begin{matrix}}{\end{matrix}\right|} \newenvironment{Vmatrix}{\left\|\begin{matrix}}{\end{matrix}\right\|} EOS |
| macro | [R] | |
| symbol_table | [R] | |
| unsecure_entity | [RW] |
# File lib/math_ml/latex.rb, line 366
366: def initialize(opt={})
367: @unsecure_entity = false
368: @entities = Hash.new
369: @commands = Hash.new
370: @symbols = Hash.new
371: @delimiters = Array.new
372: @group_begins = Hash.new
373: @group_ends = Hash.new
374: @macro = Macro.new
375: @macro.parse(BUILTIN_MACRO)
376: @expanded_command = Array.new
377: @expanded_environment = Array.new
378: @symbol_table = opt[:symbol] || MathML::Symbol::Default
379: @symbol_table = MathML::Symbol::MAP[@symbol_table] if @symbol_table.is_a?(::Symbol)
380:
381: super()
382: end
# File lib/math_ml/latex.rb, line 415
415: def add_commands(*a)
416: if a.size==1 && Hash===a[0]
417: @commands.merge!(a[0])
418: else
419: a.each{|i| @commands[i] = false}
420: end
421: end
# File lib/math_ml/latex.rb, line 431
431: def add_delimiter(list)
432: @delimiters.concat(list)
433: end
# File lib/math_ml/latex.rb, line 384
384: def add_entity(list)
385: list.each do |i|
386: @entities[i] = true
387: end
388: end
# File lib/math_ml/latex.rb, line 435
435: def add_group(begin_name, end_name, method=nil)
436: @group_begins[begin_name] = method
437: @group_ends[end_name] = begin_name
438: end
# File lib/math_ml/latex.rb, line 423
423: def add_multi_command(m, *a)
424: a.each{|i| @commands[i] = m}
425: end
# File lib/math_ml/latex.rb, line 427
427: def add_sym_cmd(hash)
428: @symbols.merge!(hash)
429: end
# File lib/math_ml/latex.rb, line 390
390: def parse(src, displaystyle=false)
391: @ds = displaystyle
392: begin
393: parse_into(src, Math.new(@ds), Font::NORMAL)
394: rescue ParseError => e
395: e.done = src[0...(src.size - e.rest.size)]
396: raise
397: end
398: end
# File lib/math_ml/latex.rb, line 400
400: def push_container(container, scanner=@scanner, font=@font)
401: data = [@container, @scanner, @font]
402: @container, @scanner, @font = [container, scanner, font]
403: begin
404: yield container
405: container
406: ensure
407: @container, @scanner, @font = data
408: end
409: end
# File lib/math_ml/latex.rb, line 591
591: def entitize(str)
592: MathML.pcstring(str.sub(/^(.*)$/){"&#{$1};"}, true)
593: end
# File lib/math_ml/latex.rb, line 467
467: def parse_any(message = "Syntax error.")
468: raise ParseError.new(message) unless @scanner.scan_any
469: s = @scanner
470: @scanner = Scanner.new(@scanner.matched)
471: begin
472: parse_to_element
473: ensure
474: @scanner = s
475: end
476: end
# File lib/math_ml/latex.rb, line 537
537: def parse_block
538: os = @scanner
539: @scanner = Scanner.new(@scanner[1])
540: begin
541: push_container(Row.new) do |r|
542: r << parse_to_element(true) until @scanner.eos?
543: end
544: rescue ParseError => e
545: e.rest << '}'
546: raise
547: ensure
548: @scanner = os
549: end
550: end
# File lib/math_ml/latex.rb, line 512
512: def parse_char
513: c = @scanner.matched
514: i = Identifier.new
515: case @font
516: when Font::ROMAN
517: i.extend(Variant).variant = Variant::NORMAL
518: when Font::BOLD
519: i.extend(Variant).variant = Variant::BOLD
520: when Font::BOLD_ITALIC
521: i.extend(Variant).variant = Variant::BOLD_ITALIC
522: when Font::BLACKBOLD
523: c = symbol_table.convert("#{c}opf")
524: when Font::SCRIPT
525: c = symbol_table.convert("#{c}scr")
526: when Font::FRAKTUR
527: c = symbol_table.convert("#{c}fr")
528: end
529: i << c
530: end
# File lib/math_ml/latex.rb, line 639
639: def parse_command
640: com = @scanner[1]
641: matched = @scanner.matched
642: pos = @scanner.pos-matched.size
643: macro = @macro.commands(com)
644: if macro
645: begin
646: flg = @expanded_command.include?(com)
647: @expanded_command.push(com)
648: raise CircularReferenceCommand if flg
649: option = (macro.option && @scanner.scan_option) ? @scanner[1] : nil
650: params = Array.new
651: (1..macro.num).each do
652: params << (@scanner.scan_block ? @scanner[1] : @scanner.scan_any)
653: raise ParseError.new("Need more parameter.") unless params.last
654: end
655: r = parse_into(@macro.expand_command(com, params, option), Array.new)
656: return r
657: rescue CircularReferenceCommand
658: if @expanded_command.size>1
659: raise
660: else
661: @scanner.pos = pos
662: raise ParseError.new("Circular reference.")
663: end
664: rescue ParseError => e
665: if @expanded_command.size>1
666: raise
667: else
668: @scanner.pos = pos
669: raise ParseError.new(%[Error in macro(#{e.message} "#{e.rest.strip}").])
670: end
671: ensure
672: @expanded_command.pop
673: end
674: elsif @commands.key?(com)
675: m = @commands[com]
676: m = com unless m
677: return __send__("cmd_#{m.to_s}")
678: end
679: parse_symbol_command(com)
680: end
# File lib/math_ml/latex.rb, line 692
692: def parse_group
693: font = @font
694: begin
695: g = @group_begins[@scanner[1]]
696: g = @scanner[1] unless g
697: __send__("grp_#{g.to_s}")
698: ensure
699: @font = font
700: end
701: end
# File lib/math_ml/latex.rb, line 441
441: def parse_into(src, parent, font=nil)
442: orig = [@scanner, @container, @font, @ds]
443: @scanner = Scanner.new(src)
444: @container = parent
445: @font = font if font
446: begin
447: until @scanner.eos?
448: @container << parse_to_element(true)
449: end
450: @container
451: rescue BlockNotClosed => e
452: raise ParseError.new("Block not closed.", @scanner.rest)
453: rescue NotEnvironment => e
454: raise ParseError.new("Not environment.", @scanner.rest)
455: rescue EnvironmentNotEnd => e
456: raise ParseError.new("Environment not end.", @scanner.rest)
457: rescue OptionNotClosed => e
458: raise ParseError.new("Option not closed.", @scanner.rest)
459: rescue ParseError => e
460: e.rest << @scanner.rest.to_s
461: raise
462: ensure
463: @scanner, @container, @font, @ds = orig
464: end
465: end
# File lib/math_ml/latex.rb, line 682
682: def parse_mathfont(font)
683: f = @font
684: @font = font
685: begin
686: push_container(Row.new){|r| r << parse_any}
687: ensure
688: @font = f
689: end
690: end
# File lib/math_ml/latex.rb, line 506
506: def parse_num
507: n = Number.new
508: n.extend(Variant).variant = Variant::BOLD if @font==Font::BOLD
509: n << @scanner.matched
510: end
# File lib/math_ml/latex.rb, line 532
532: def parse_operator
533: o = @scanner.matched
534: Operator.new << o
535: end
# File lib/math_ml/latex.rb, line 552
552: def parse_sub
553: e = @container.pop
554: e = None.new unless e
555: e = SubSup.new(@ds && e.display_style, e) unless e.is_a?(SubSup)
556: raise ParseError.new("Double subscript.", "_") if e.sub
557: e.sub = parse_any("Subscript not exist.")
558: e
559: end
# File lib/math_ml/latex.rb, line 561
561: def parse_sup
562: e = @container.pop
563: e = None.new unless e
564: e = SubSup.new(@ds && e.display_style, e) unless e.is_a?(SubSup)
565: raise ParseError.new("Double superscript.", @scanner[0]) if e.sup
566: if /'+/=~@scanner[0]
567: prime = Operator.new
568: @scanner[0].size.times do
569: prime << symbol_table.convert("prime")
570: end
571: unless @scanner.scan(/\^/)
572: e.sup = prime
573: return e
574: end
575: end
576: sup = parse_any("Superscript not exist.")
577:
578: if prime
579: unless sup.is_a?(Row)
580: r = Row.new
581: r << sup
582: sup = r
583: end
584: sup.contents.insert(0, prime)
585: end
586:
587: e.sup = sup
588: e
589: end
# File lib/math_ml/latex.rb, line 595
595: def parse_symbol_command(com, plain=false)
596: unless @symbols.include?(com)
597: @scanner.pos = @scanner.pos-(com.size+1)
598: raise ParseError.new("Undefined command.")
599: end
600: data = @symbols[com]
601: return nil unless data
602:
603: data, s = data
604: su = data[0]
605: el = data[1]
606: el = :o unless el
607: s = com.dup.untaint.to_sym unless s
608: s = com if s.is_a?(String) && s.length==0
609:
610: case el
611: when :I
612: el = Identifier.new
613: when :i
614: el = Identifier.new
615: el.extend(Variant).variant = Variant::NORMAL unless s.is_a?(String)&&s.length>1
616: when :o
617: el = Operator.new
618: when :n
619: el = Number.new
620: else
621: raise ParseError.new("Inner data broken.")
622: end
623:
624: case s
625: when Fixnum
626: s = MathML.pcstring("&\#x#{s.to_s(16)};", true)
627: when ::Symbol
628: s = symbol_table.convert(s)
629: else
630: MathML.pcstring(s, true)
631: end
632:
633: return s if plain
634: el << s
635: el.as_display_style if su==:u
636: el
637: end
# File lib/math_ml/latex.rb, line 478
478: def parse_to_element(whole_group = false)
479: if whole_group && @group_begins.has_key?(@scanner.peek_command)
480: @scanner.scan_command
481: parse_group
482: else
483: case
484: when @scanner.scan(RE::NUMERICS)
485: parse_num
486: when @scanner.scan(RE::ALPHABETS)
487: parse_char
488: when @scanner.scan(RE::OPERATORS)
489: parse_operator
490: when @scanner.scan_block
491: parse_block
492: when @scanner.scan(/_/)
493: parse_sub
494: when @scanner.scan(/'+|\^/)
495: parse_sup
496: when @scanner.scan(/~/)
497: Space.new("1em")
498: when @scanner.scan_command
499: parse_command
500: else
501: raise ParseError.new('Syntax error.')
502: end
503: end
504: end