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(%[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 = 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