# class MathML::LaTeX::Parser

### Constants

BUILTIN_MACRO

### Attributes

macro[R]
symbol_table[R]
unsecure_entity[RW]

### Public Class Methods

new(opt={}) click to toggle source
Calls superclass method MathML::LaTeX::BuiltinCommands.new

    # File lib/math_ml/latex.rb, line 365
    def initialize(opt={})
            @unsecure_entity = false
            @entities = Hash.new
            @commands = Hash.new
            @symbols = Hash.new
            @delimiters = Array.new
            @group_begins = Hash.new
            @group_ends = Hash.new
            @macro = Macro.new
            @macro.parse(BUILTIN_MACRO)
            @expanded_command = Array.new
            @expanded_environment = Array.new
            @symbol_table = opt[:symbol] || MathML::Symbol::Default
            @symbol_table = MathML::Symbol::MAP[@symbol_table] if @symbol_table.is_a?(::Symbol)

            super()
    end

### Public Instance Methods

add_commands(*a) click to toggle source

    # File lib/math_ml/latex.rb, line 414
    def add_commands(*a)
            if a.size==1 && Hash===a[0]
                    @commands.merge!(a[0])
            else
                    a.each{|i| @commands[i] = false}
            end
    end

add_delimiter(list) click to toggle source

    # File lib/math_ml/latex.rb, line 430
    def add_delimiter(list)
            @delimiters.concat(list)
    end

add_entity(list) click to toggle source

    # File lib/math_ml/latex.rb, line 383
    def add_entity(list)
            list.each do |i|
                    @entities[i] = true
            end
    end

add_group(begin_name, end_name, method=nil) click to toggle source

    # File lib/math_ml/latex.rb, line 434
    def add_group(begin_name, end_name, method=nil)
            @group_begins[begin_name] = method
            @group_ends[end_name] = begin_name
    end

add_multi_command(m, *a) click to toggle source

    # File lib/math_ml/latex.rb, line 422
    def add_multi_command(m, *a)
            a.each{|i| @commands[i] = m}
    end

add_plugin(plugin) click to toggle source

    # File lib/math_ml/latex.rb, line 410
    def add_plugin(plugin)
            self.extend(plugin)
    end

add_sym_cmd(hash) click to toggle source

    # File lib/math_ml/latex.rb, line 426
    def add_sym_cmd(hash)
            @symbols.merge!(hash)
    end

parse(src, displaystyle=false) click to toggle source

    # File lib/math_ml/latex.rb, line 389
    def parse(src, displaystyle=false)
            @ds = displaystyle
            begin
                    parse_into(src, Math.new(@ds), Font::NORMAL)
            rescue ParseError => e
                    e.done = src[0...(src.size - e.rest.size)]
                    raise
            end
    end

push_container(container, scanner=@scanner, font=@font) { |container| ... } click to toggle source

    # File lib/math_ml/latex.rb, line 399
    def push_container(container, scanner=@scanner, font=@font)
            data = [@container, @scanner, @font]
            @container, @scanner, @font = [container, scanner, font]
            begin
                    yield container
                    container
            ensure
                    @container, @scanner, @font = data
            end
    end

### Private Instance Methods

entitize(str) click to toggle source

    # File lib/math_ml/latex.rb, line 590
    def entitize(str)
            MathML.pcstring(str.sub(/^(.*)$/){"&#{$1};"}, true)
    end

parse_any(message = "Syntax error.") click to toggle source

    # File lib/math_ml/latex.rb, line 466
    def parse_any(message = "Syntax error.")
            raise ParseError.new(message) unless @scanner.scan_any
            s = @scanner
            @scanner = Scanner.new(@scanner.matched)
            begin
                    parse_to_element
            ensure
                    @scanner = s
            end
    end

parse_block() click to toggle source

    # File lib/math_ml/latex.rb, line 536
    def parse_block
            os = @scanner
            @scanner =  Scanner.new(@scanner[1])
            begin
                    push_container(Row.new) do |r|
                            r << parse_to_element(true) until @scanner.eos?
                    end
            rescue ParseError => e
                    e.rest << '}'
                    raise
            ensure
                    @scanner = os
            end
    end

parse_char() click to toggle source

    # File lib/math_ml/latex.rb, line 511
    def parse_char
            c = @scanner.matched
            i = Identifier.new
            case @font
            when Font::ROMAN
                    i.extend(Variant).variant = Variant::NORMAL
            when Font::BOLD
                    i.extend(Variant).variant = Variant::BOLD
            when Font::BOLD_ITALIC
                    i.extend(Variant).variant = Variant::BOLD_ITALIC
            when Font::BLACKBOLD
                    c = symbol_table.convert("#{c}opf")
            when Font::SCRIPT
                    c = symbol_table.convert("#{c}scr")
            when Font::FRAKTUR
                    c = symbol_table.convert("#{c}fr")
            end
            i << c
    end

parse_command() click to toggle source

    # File lib/math_ml/latex.rb, line 639
    def parse_command
            com = @scanner[1]
            matched = @scanner.matched
            pos = @scanner.pos-matched.size
            macro = @macro.commands(com)
            if macro
                    begin
                            flg = @expanded_command.include?(com)
                            @expanded_command.push(com)
                            raise CircularReferenceCommand if flg
                            option = (macro.option && @scanner.scan_option) ? @scanner[1] : nil
                            params = Array.new
                            (1..macro.num).each do
                                    params << (@scanner.scan_block ? @scanner[1] : @scanner.scan_any)
                                    raise ParseError.new("Need more parameter.") unless params.last
                            end
                            r = parse_into(@macro.expand_command(com, params, option), Array.new)
                            return r
                    rescue CircularReferenceCommand
                            if @expanded_command.size>1
                                    raise
                            else
                                    @scanner.pos = pos
                                    raise ParseError.new("Circular reference.")
                            end
                    rescue ParseError => e
                            if @expanded_command.size>1
                                    raise
                            else
                                    @scanner.pos = pos
                                    raise ParseError.new(%Q[Error in macro(#{e.message} "#{e.rest.strip}").])
                            end
                    ensure
                            @expanded_command.pop
                    end
            elsif @commands.key?(com)
                    m = @commands[com]
                    m = com unless m
                    return __send__("cmd_#{m.to_s}")
            end
            parse_symbol_command(com)
    end

parse_group() click to toggle source

    # File lib/math_ml/latex.rb, line 692
    def parse_group
            font = @font
            begin
                    g = @group_begins[@scanner[1]]
                    g = @scanner[1] unless g
                    __send__("grp_#{g.to_s}")
            ensure
                    @font = font
            end
    end

parse_into(src, parent, font=nil) click to toggle source

    # File lib/math_ml/latex.rb, line 440
    def parse_into(src, parent, font=nil)
            orig = [@scanner, @container, @font, @ds]
            @scanner = Scanner.new(src)
            @container = parent
            @font = font if font
            begin
                    until @scanner.eos?
                            @container << parse_to_element(true)
                    end
                    @container
            rescue BlockNotClosed => e
                    raise  ParseError.new("Block not closed.", @scanner.rest)
            rescue NotEnvironment => e
                    raise ParseError.new("Not environment.", @scanner.rest)
            rescue EnvironmentNotEnd => e
                    raise ParseError.new("Environment not end.", @scanner.rest)
            rescue OptionNotClosed => e
                    raise ParseError.new("Option not closed.", @scanner.rest)
            rescue ParseError => e
                    e.rest << @scanner.rest.to_s
                    raise
            ensure
                    @scanner, @container, @font, @ds = orig
            end
    end

parse_mathfont(font) click to toggle source

    # File lib/math_ml/latex.rb, line 682
    def parse_mathfont(font)
            f = @font
            @font = font
            begin
                    push_container(Row.new){|r| r << parse_any}
            ensure
                    @font = f
            end
    end

parse_num() click to toggle source

    # File lib/math_ml/latex.rb, line 505
    def parse_num
            n = Number.new
            n.extend(Variant).variant = Variant::BOLD if @font==Font::BOLD
            n << @scanner.matched
    end

parse_operator() click to toggle source

    # File lib/math_ml/latex.rb, line 531
    def parse_operator
            o = @scanner.matched
            Operator.new.tap{|op| op[:stretchy]="false"} << o
    end

parse_sub() click to toggle source

    # File lib/math_ml/latex.rb, line 551
    def parse_sub
            e = @container.pop
            e = None.new unless e
            e = SubSup.new(@ds && e.display_style, e) unless e.is_a?(SubSup)
            raise ParseError.new("Double subscript.", "_") if e.sub
            e.sub = parse_any("Subscript not exist.")
            e
    end

parse_sup() click to toggle source

    # File lib/math_ml/latex.rb, line 560
    def parse_sup
            e = @container.pop
            e = None.new unless e
            e = SubSup.new(@ds && e.display_style, e) unless e.is_a?(SubSup)
            raise ParseError.new("Double superscript.", @scanner[0]) if e.sup
            if /'+/=~@scanner[0]
                    prime = Operator.new
                    @scanner[0].size.times do
                            prime << symbol_table.convert("prime")
                    end
                    unless @scanner.scan(/\^/)
                            e.sup = prime
                            return e
                    end
            end
            sup = parse_any("Superscript not exist.")

            if prime
                    unless sup.is_a?(Row)
                            r = Row.new
                            r << sup
                            sup = r
                    end
                    sup.contents.insert(0, prime)
            end

            e.sup = sup
            e
    end

parse_symbol_command(com, plain=false) click to toggle source

    # File lib/math_ml/latex.rb, line 594
    def parse_symbol_command(com, plain=false)
            unless @symbols.include?(com)
                    @scanner.pos = @scanner.pos-(com.size+1)
                    raise ParseError.new("Undefined command.")
            end
            data = @symbols[com]
            return nil unless data

            data, s = data
            su = data[0]
            el = data[1]
            el = :o unless el
            s = com.dup.untaint.to_sym unless s
            s = com if s.is_a?(String) && s.length==0

            case el
            when :I
                    el = Identifier.new
            when :i
                    el = Identifier.new
                    el.extend(Variant).variant = Variant::NORMAL unless s.is_a?(String)&&s.length>1
            when :o
                    el = Operator.new
                    el[:stretchy] = "false"
            when :n
                    el = Number.new
            else
                    raise ParseError.new("Inner data broken.")
            end

            case s
            when Fixnum
                    s = MathML.pcstring("&\#x#{s.to_s(16)};", true)
            when ::Symbol
                    s = symbol_table.convert(s)
            else
                    MathML.pcstring(s, true)
            end

            return s if plain
            el << s
            el.as_display_style if su==:u
            el
    end

parse_to_element(whole_group = false) click to toggle source

    # File lib/math_ml/latex.rb, line 477
    def parse_to_element(whole_group = false)
            if whole_group && @group_begins.has_key?(@scanner.peek_command)
                    @scanner.scan_command
                    parse_group
            else
                    case
                    when @scanner.scan(RE::NUMERICS)
                            parse_num
                    when @scanner.scan(RE::ALPHABETS)
                            parse_char
                    when @scanner.scan(RE::OPERATORS)
                            parse_operator
                    when @scanner.scan_block
                            parse_block
                    when @scanner.scan(/_/)
                            parse_sub
                    when @scanner.scan(/'+|\^/)
                            parse_sup
                    when @scanner.scan(/~/)
                            Space.new("1em")
                    when @scanner.scan_command
                            parse_command
                    else
                            raise ParseError.new('Syntax error.')
                    end
            end
    end