class AsciiMath::Parser

Constants

DEFAULT_COLOR_TABLE
DEFAULT_PARSER_SYMBOL_TABLE

Public Class Methods

add_default_colors(b) click to toggle source
# File lib/asciimath/parser.rb, line 201
def self.add_default_colors(b)
  b.add('aqua', 0, 255, 255)
  b.add('black', 0, 0, 0)
  b.add('blue', 0, 0, 255)
  b.add('fuchsia', 255, 0, 255)
  b.add('gray', 128, 128, 128)
  b.add('green', 0, 128, 0)
  b.add('lime', 0, 255, 0)
  b.add('maroon', 128, 0, 0)
  b.add('navy', 0, 0, 128)
  b.add('olive', 128, 128, 0)
  b.add('purple', 128, 0, 128)
  b.add('red', 255, 0, 0)
  b.add('silver', 192, 192, 192)
  b.add('teal', 0, 128, 128)
  b.add('white', 255, 255, 255)
  b.add('yellow', 255, 255, 0)
  b
end
add_default_parser_symbols(b) click to toggle source
# File lib/asciimath/parser.rb, line 221
def self.add_default_parser_symbols(b)
  # Operation symbols
  b.add('+', :plus, :symbol)
  b.add('-', :minus, :symbol)
  b.add('*', 'cdot', :cdot, :symbol)
  b.add('**', 'ast', :ast, :symbol)
  b.add('***', 'star', :star, :symbol)
  b.add('//', :slash, :symbol)
  b.add('\\\\', 'backslash', :backslash, :symbol)
  b.add('setminus', :setminus, :symbol)
  b.add('xx', 'times', :times, :symbol)
  b.add('|><', 'ltimes', :ltimes, :symbol)
  b.add('><|', 'rtimes', :rtimes, :symbol)
  b.add('|><|', 'bowtie', :bowtie, :symbol)
  b.add('-:', 'div', 'divide', :div, :symbol)
  b.add('@', 'circ', :circ, :symbol)
  b.add('o+', 'oplus', :oplus, :symbol)
  b.add('ox', 'otimes', :otimes, :symbol)
  b.add('o.', 'odot', :odot, :symbol)
  b.add('sum', :sum, :symbol)
  b.add('prod', :prod, :symbol)
  b.add('^^', 'wedge', :wedge, :symbol)
  b.add('^^^', 'bigwedge', :bigwedge, :symbol)
  b.add('vv', 'vee', :vee, :symbol)
  b.add('vvv', 'bigvee', :bigvee, :symbol)
  b.add('nn', 'cap', :cap, :symbol)
  b.add('nnn', 'bigcap', :bigcap, :symbol)
  b.add('uu', 'cup', :cup, :symbol)
  b.add('uuu', 'bigcup', :bigcup, :symbol)

  # Relation symbols
  b.add('=', :eq, :symbol)
  b.add('!=', 'ne', :ne, :symbol)
  b.add(':=', :assign, :symbol)
  b.add('<', 'lt', :lt, :symbol)
  b.add('>', 'gt', :gt, :symbol)
  b.add('<=', 'le', :le, :symbol)
  b.add('>=', 'ge', :ge, :symbol)
  b.add('-<', '-lt', 'prec', :prec, :symbol)
  b.add('>-', 'succ', :succ, :symbol)
  b.add('-<=', 'preceq', :preceq, :symbol)
  b.add('>-=', 'succeq', :succeq, :symbol)
  b.add('in', :in, :symbol)
  b.add('!in', 'notin', :notin, :symbol)
  b.add('sub', 'subset', :subset, :symbol)
  b.add('sup', 'supset', :supset, :symbol)
  b.add('sube', 'subseteq', :subseteq, :symbol)
  b.add('supe', 'supseteq', :supseteq, :symbol)
  b.add('-=', 'equiv', :equiv, :symbol)
  b.add('~=', 'cong', :cong, :symbol)
  b.add('~~', 'approx', :approx, :symbol)
  b.add('prop', 'propto', :propto, :symbol)

  # Logical symbols
  b.add('and', :and, :symbol)
  b.add('or', :or, :symbol)
  b.add('not', 'neg', :not, :symbol)
  b.add('=>', 'implies', :implies, :symbol)
  b.add('if', :if, :symbol)
  b.add('<=>', 'iff', :iff, :symbol)
  b.add('AA', 'forall', :forall, :symbol)
  b.add('EE', 'exists', :exists, :symbol)
  b.add('_|_', 'bot', :bot, :symbol)
  b.add('TT', 'top', :top, :symbol)
  b.add('|--', 'vdash', :vdash, :symbol)
  b.add('|==', 'models', :models, :symbol)

  # Grouping brackets
  b.add('(', 'left(', :lparen, :lparen)
  b.add(')', 'right)', :rparen, :rparen)
  b.add('[', 'left[', :lbracket, :lparen)
  b.add(']', 'right]', :rbracket, :rparen)
  b.add('{', :lbrace, :lparen)
  b.add('}', :rbrace, :rparen)
  b.add('|', :vbar, :lrparen)
  b.add(':|:', :vbar, :symbol)
  b.add('|:', :vbar, :lparen)
  b.add(':|', :vbar, :rparen)
  # b.add('||', '||', :lrparen)
  b.add('(:', '<<', 'langle', :langle, :lparen)
  b.add(':)', '>>', 'rangle', :rangle, :rparen)
  b.add('{:', nil, :lparen)
  b.add(':}', nil, :rparen)

  # Miscellaneous symbols
  b.add('int', :integral, :symbol)
  b.add('dx', :dx, :symbol)
  b.add('dy', :dy, :symbol)
  b.add('dz', :dz, :symbol)
  b.add('dt', :dt, :symbol)
  b.add('oint', :contourintegral, :symbol)
  b.add('del', 'partial', :partial, :symbol)
  b.add('grad', 'nabla', :nabla, :symbol)
  b.add('+-', 'pm', :pm, :symbol)
  b.add('O/', 'emptyset', :emptyset, :symbol)
  b.add('oo', 'infty', :infty, :symbol)
  b.add('aleph', :aleph, :symbol)
  b.add('...', 'ldots', :ellipsis, :symbol)
  b.add(':.', 'therefore', :therefore, :symbol)
  b.add(':\'', 'because', :because, :symbol)
  b.add('/_', 'angle', :angle, :symbol)
  b.add('/_\\', 'triangle', :triangle, :symbol)
  b.add('\'', 'prime', :prime, :symbol)
  b.add('tilde', :tilde, :unary)
  b.add('\\ ', :nbsp, :symbol)
  b.add('frown', :frown, :symbol)
  b.add('quad', :quad, :symbol)
  b.add('qquad', :qquad, :symbol)
  b.add('cdots', :cdots, :symbol)
  b.add('vdots', :vdots, :symbol)
  b.add('ddots', :ddots, :symbol)
  b.add('diamond', :diamond, :symbol)
  b.add('square', :square, :symbol)
  b.add('|__', 'lfloor', :lfloor, :symbol)
  b.add('__|', 'rfloor', :rfloor, :symbol)
  b.add('|~', 'lceiling', :lceiling, :symbol)
  b.add('~|', 'rceiling', :rceiling, :symbol)
  b.add('CC', :dstruck_captial_c, :symbol)
  b.add('NN', :dstruck_captial_n, :symbol)
  b.add('QQ', :dstruck_captial_q, :symbol)
  b.add('RR', :dstruck_captial_r, :symbol)
  b.add('ZZ', :dstruck_captial_z, :symbol)
  b.add('f', :f, :symbol)
  b.add('g', :g, :symbol)


  # Standard functions
  b.add('lim', :lim, :symbol)
  b.add('Lim', :Lim, :symbol)
  b.add('min', :min, :symbol)
  b.add('max', :max, :symbol)
  b.add('sin', :sin, :symbol)
  b.add('Sin', :Sin, :symbol)
  b.add('cos', :cos, :symbol)
  b.add('Cos', :Cos, :symbol)
  b.add('tan', :tan, :symbol)
  b.add('Tan', :Tan, :symbol)
  b.add('sinh', :sinh, :symbol)
  b.add('Sinh', :Sinh, :symbol)
  b.add('cosh', :cosh, :symbol)
  b.add('Cosh', :Cosh, :symbol)
  b.add('tanh', :tanh, :symbol)
  b.add('Tanh', :Tanh, :symbol)
  b.add('cot', :cot, :symbol)
  b.add('Cot', :Cot, :symbol)
  b.add('sec', :sec, :symbol)
  b.add('Sec', :Sec, :symbol)
  b.add('csc', :csc, :symbol)
  b.add('Csc', :Csc, :symbol)
  b.add('arcsin', :arcsin, :symbol)
  b.add('arccos', :arccos, :symbol)
  b.add('arctan', :arctan, :symbol)
  b.add('coth', :coth, :symbol)
  b.add('sech', :sech, :symbol)
  b.add('csch', :csch, :symbol)
  b.add('exp', :exp, :symbol)
  b.add('abs', :abs, :unary)
  b.add('Abs', :abs, :unary)
  b.add('norm', :norm, :unary)
  b.add('floor', :floor, :unary)
  b.add('ceil', :ceil, :unary)
  b.add('log', :log, :symbol)
  b.add('Log', :Log, :symbol)
  b.add('ln', :ln, :symbol)
  b.add('Ln', :Ln, :symbol)
  b.add('det', :det, :symbol)
  b.add('dim', :dim, :symbol)
  b.add('ker', :ker, :symbol)
  b.add('mod', :mod, :symbol)
  b.add('gcd', :gcd, :symbol)
  b.add('lcm', :lcm, :symbol)
  b.add('lub', :lub, :symbol)
  b.add('glb', :glb, :symbol)

  # Arrows
  b.add('uarr', 'uparrow', :uparrow, :symbol)
  b.add('darr', 'downarrow', :downarrow, :symbol)
  b.add('rarr', 'rightarrow', :rightarrow, :symbol)
  b.add('->', 'to', :to, :symbol)
  b.add('>->', 'rightarrowtail', :rightarrowtail, :symbol)
  b.add('->>', 'twoheadrightarrow', :twoheadrightarrow, :symbol)
  b.add('>->>', 'twoheadrightarrowtail', :twoheadrightarrowtail, :symbol)
  b.add('|->', 'mapsto', :mapsto, :symbol)
  b.add('larr', 'leftarrow', :leftarrow, :symbol)
  b.add('harr', 'leftrightarrow', :leftrightarrow, :symbol)
  b.add('rArr', 'Rightarrow', :Rightarrow, :symbol)
  b.add('lArr', 'Leftarrow', :Leftarrow, :symbol)
  b.add('hArr', 'Leftrightarrow', :Leftrightarrow, :symbol)

  # Other
  b.add('sqrt', :sqrt, :unary)
  b.add('root', :root, :binary)
  b.add('frac', :frac, :binary)
  b.add('/', :frac, :infix)
  b.add('stackrel', :stackrel, :binary)
  b.add('overset', :overset, :binary)
  b.add('underset', :underset, :binary)
  b.add('color', :color, :binary, :convert_operand1 => ::AsciiMath::Parser.instance_method(:convert_to_color))
  b.add('_', :sub, :infix)
  b.add('^', :sup, :infix)
  b.add('hat', :hat, :unary)
  b.add('bar', :overline, :unary)
  b.add('vec', :vec, :unary)
  b.add('dot', :dot, :unary)
  b.add('ddot', :ddot, :unary)
  b.add('overarc', 'overparen', :overarc, :unary)
  b.add('ul', 'underline', :underline, :unary)
  b.add('ubrace', 'underbrace', :underbrace, :unary)
  b.add('obrace', 'overbrace', :overbrace, :unary)
  b.add('cancel', :cancel, :unary)
  b.add('bb', :bold, :unary)
  b.add('bbb', :double_struck, :unary)
  b.add('ii', :italic, :unary)
  b.add('bii', :bold_italic, :unary)
  b.add('cc', :script, :unary)
  b.add('bcc', :bold_script, :unary)
  b.add('tt', :monospace, :unary)
  b.add('fr', :fraktur, :unary)
  b.add('bfr', :bold_fraktur, :unary)
  b.add('sf', :sans_serif, :unary)
  b.add('bsf', :bold_sans_serif, :unary)
  b.add('sfi', :sans_serif_italic, :unary)
  b.add('sfbi', :sans_serif_bold_italic, :unary)
  b.add('rm', :roman, :unary)

  # Greek letters
  b.add('alpha', :alpha, :symbol)
  b.add('Alpha', :Alpha, :symbol)
  b.add('beta', :beta, :symbol)
  b.add('Beta', :Beta, :symbol)
  b.add('gamma', :gamma, :symbol)
  b.add('Gamma', :Gamma, :symbol)
  b.add('delta', :delta, :symbol)
  b.add('Delta', :Delta, :symbol)
  b.add('epsi', 'epsilon', :epsilon, :symbol)
  b.add('Epsilon', :Epsilon, :symbol)
  b.add('varepsilon', :varepsilon, :symbol)
  b.add('zeta', :zeta, :symbol)
  b.add('Zeta', :Zeta, :symbol)
  b.add('eta', :eta, :symbol)
  b.add('Eta', :Eta, :symbol)
  b.add('theta', :theta, :symbol)
  b.add('Theta', :Theta, :symbol)
  b.add('vartheta', :vartheta, :symbol)
  b.add('iota', :iota, :symbol)
  b.add('Iota', :Iota, :symbol)
  b.add('kappa', :kappa, :symbol)
  b.add('Kappa', :Kappa, :symbol)
  b.add('lambda', :lambda, :symbol)
  b.add('Lambda', :Lambda, :symbol)
  b.add('mu', :mu, :symbol)
  b.add('Mu', :Mu, :symbol)
  b.add('nu', :nu, :symbol)
  b.add('Nu', :Nu, :symbol)
  b.add('xi', :xi, :symbol)
  b.add('Xi', :Xi, :symbol)
  b.add('omicron', :omicron, :symbol)
  b.add('Omicron', :Omicron, :symbol)
  b.add('pi', :pi, :symbol)
  b.add('Pi', :Pi, :symbol)
  b.add('rho', :rho, :symbol)
  b.add('Rho', :Rho, :symbol)
  b.add('sigma', :sigma, :symbol)
  b.add('Sigma', :Sigma, :symbol)
  b.add('tau', :tau, :symbol)
  b.add('Tau', :Tau, :symbol)
  b.add('upsilon', :upsilon, :symbol)
  b.add('Upsilon', :Upsilon, :symbol)
  b.add('phi', :phi, :symbol)
  b.add('Phi', :Phi, :symbol)
  b.add('varphi', :varphi, :symbol)
  b.add('chi', :chi, :symbol)
  b.add('Chi', :Chi, :symbol)
  b.add('psi', :psi, :symbol)
  b.add('Psi', :Psi, :symbol)
  b.add('omega', :omega, :symbol)
  b.add('Omega', :Omega, :symbol)

  b
end
new(symbol_table, color_table) click to toggle source
# File lib/asciimath/parser.rb, line 502
def initialize(symbol_table, color_table)
  @symbol_table = symbol_table
  @color_table = color_table
end

Public Instance Methods

parse(input) click to toggle source
# File lib/asciimath/parser.rb, line 507
def parse(input)
  Expression.new(
      input,
      parse_expression(Tokenizer.new(input, @symbol_table), 0)
  )
end

Private Instance Methods

append_color_text(s, node) click to toggle source
# File lib/asciimath/parser.rb, line 725
def append_color_text(s, node)
  case node
    when ::AsciiMath::AST::Sequence
      node.each { |n| append_color_text(s, n) }
    when ::AsciiMath::AST::Number, ::AsciiMath::AST::Identifier, ::AsciiMath::AST::Text
      s << node.value
    when ::AsciiMath::AST::Symbol
      s << node.text
    when ::AsciiMath::AST::Group
      append_color_text(s, node.expression)
    when ::AsciiMath::AST::Paren
      append_color_text(s, node.lparen)
      append_color_text(s, node.expression)
      append_color_text(s, node.rparen)
    when ::AsciiMath::AST::SubSup
      append_color_text(s, node.base_expression)
      append_color_text(s, node.operator)
      append_color_text(s, node.operand2)
    when ::AsciiMath::AST::UnaryOp
      append_color_text(s, node.operator)
      append_color_text(s, node.operand)
    when ::AsciiMath::AST::BinaryOp
      append_color_text(s, node.operator)
      append_color_text(s, node.operand1)
      append_color_text(s, node.operand2)
    when ::AsciiMath::AST::InfixOp
      append_color_text(s, node.operand1)
      append_color_text(s, node.operator)
      append_color_text(s, node.operand2)
  end
end
convert_node(node, converter) click to toggle source
# File lib/asciimath/parser.rb, line 698
def convert_node(node, converter)
  case converter
    when nil
      node
    when UnboundMethod
      converter.bind(self).call(node)
    when Method, Proc
      converter.call(node)
  end
end
convert_to_color(color_expression) click to toggle source
# File lib/asciimath/parser.rb, line 709
def convert_to_color(color_expression)
  s = ""
  append_color_text(s, color_expression)

  case s
    when /#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i
      color_value = {:r => $1.to_i(16), :g => $2.to_i(16), :b => $3.to_i(16), }
    when /#([0-9a-f])([0-9a-f])([0-9a-f])/i
      color_value = {:r => "#{$1}#{$1}".to_i(16), :g => "#{$2}#{$2}".to_i(16), :b => "#{$3}#{$3}".to_i(16), }
    else
      color_value = @color_table[s.downcase] || {:r => 0, :g => 0, :b => 0}
  end

  color(color_value[:r], color_value[:g], color_value[:b], s)
end
convert_to_matrix(node) click to toggle source
# File lib/asciimath/parser.rb, line 656
def convert_to_matrix(node)
  return node unless node.is_a?(::AsciiMath::AST::Paren) && node.expression.is_a?(::AsciiMath::AST::Sequence)

  rows, separators = node.expression.partition.with_index { |obj, i| i.even? }
  return node unless rows.length > 1 &&
      rows.length > separators.length &&
      separators.all? { |item| is_matrix_separator(item) } &&
      (rows.all? { |item| item.is_a?(::AsciiMath::AST::Paren) && item.lparen == symbol(:lparen, '(') && item.rparen == symbol(:rparen, ')') } ||
          rows.all? { |item| item.is_a?(::AsciiMath::AST::Paren) && item.lparen == symbol(:lbracket, '[') && item.rparen == symbol(:rbracket, ']') })

  rows = rows.map do |row|
    chunks = []
    current_chunk = []

    row_content = row.expression
    unless row_content.is_a?(::AsciiMath::AST::Sequence)
      [expression(row_content)]
    else
      row_content.each do |item|
        if is_matrix_separator(item)
          chunks << current_chunk
          current_chunk = []
        else
          current_chunk << item
        end
      end

      chunks << current_chunk

      chunks.map { |c| c.length == 1 ? c[0] : expression(*c) }.to_a
    end
  end

  return node unless rows.all? { |row| row.length == rows[0].length }

  matrix(node.lparen, rows, node.rparen)
end
is_matrix_separator(node) click to toggle source
# File lib/asciimath/parser.rb, line 694
def is_matrix_separator(node)
  node.is_a?(Identifier) && node.value == ','
end
parse_expression(tok, depth) click to toggle source
# File lib/asciimath/parser.rb, line 518
def parse_expression(tok, depth)
  e = []

  while (s1 = parse_intermediate_expression(tok, depth))
    t1 = tok.next_token

    if t1[:type] == :infix && t1[:value] == :frac
      s2 = parse_intermediate_expression(tok, depth)
      if s2
        e << infix(unwrap_paren(s1), symbol(:frac, t1[:text]), unwrap_paren(s2))
      else
        e << s1
      end
    elsif t1[:type] == :eof
      e << s1
      break
    else
      e << s1
      tok.push_back(t1)
      if (t1[:type] == :lrparen || t1[:type] == :rparen) && depth > 0
        break
      end
    end
  end

  expression(*e)
end
parse_intermediate_expression(tok, depth) click to toggle source
# File lib/asciimath/parser.rb, line 546
def parse_intermediate_expression(tok, depth)
  s = parse_simple_expression(tok, depth)
  sub = nil
  sup = nil

  t1 = tok.next_token
  case t1[:type]
    when :infix
      case t1[:value]
        when :sub
          sub = parse_simple_expression(tok, depth)
          if sub
            t2 = tok.next_token
            if t2[:type] == :infix && t2[:value] == :sup
              sup = parse_simple_expression(tok, depth)
            else
              tok.push_back(t2)
            end
          end
        when :sup
          sup = parse_simple_expression(tok, depth)
        else
          tok.push_back(t1)
      end
    else
      tok.push_back(t1)
  end

  if sub && sup
    subsup(s, unwrap_paren(sub), unwrap_paren(sup))
  elsif sub
    sub(s, unwrap_paren(sub))
  elsif sup
    sup(s, unwrap_paren(sup))
  else
    s
  end
end
parse_simple_expression(tok, depth) click to toggle source
# File lib/asciimath/parser.rb, line 585
def parse_simple_expression(tok, depth)
  t1 = tok.next_token

  case t1[:type]
    when :lparen, :lrparen
      t2 = tok.next_token
      case t2[:type]
        when :rparen, :lrparen
          paren(token_to_symbol(t1), nil, token_to_symbol(t2))
        else
          tok.push_back(t2)

          e = parse_expression(tok, depth + 1)

          t2 = tok.next_token
          case t2[:type]
            when :rparen, :lrparen
              convert_to_matrix(paren(token_to_symbol(t1), e, token_to_symbol(t2)))
            else
              tok.push_back(t2)
              paren(token_to_symbol(t1), e, nil)
          end
      end
    when :rparen
      if depth > 0
        tok.push_back(t1)
        nil
      else
        token_to_symbol(t1)
      end
    when :unary
      parse_simple_expression = parse_simple_expression(tok, depth)
      s = unwrap_paren(parse_simple_expression)
      s = identifier('') if s.nil?
      s = convert_node(s, t1[:convert_operand])
      unary(token_to_symbol(t1), s)
    when :binary
      s1 = unwrap_paren(parse_simple_expression(tok, depth))
      s1 = identifier('') if s1.nil?
      s2 = unwrap_paren(parse_simple_expression(tok, depth))
      s2 = identifier('') if s2.nil?

      s1 = convert_node(s1, t1[:convert_operand1])
      s2 = convert_node(s2, t1[:convert_operand2])

      binary(token_to_symbol(t1), s1, s2)
    when :eof
      nil
    when :number
      number(t1[:value])
    when :text
      text(t1[:value])
    when :identifier
      identifier(t1[:value])
    else
      token_to_symbol(t1)
  end
end
token_to_symbol(t1) click to toggle source
# File lib/asciimath/parser.rb, line 644
def token_to_symbol(t1)
  symbol(t1[:value], t1[:text])
end
unwrap_paren(node) click to toggle source
# File lib/asciimath/parser.rb, line 648
def unwrap_paren(node)
  if node.is_a?(::AsciiMath::AST::Paren)
    group(node.lparen, node.expression, node.rparen)
  else
    node
  end
end