class Ritex::Parser

The parser for itex and the main entry point for Ritex. This class is partially defined here and partially generated by Racc from lib/parser.y.

Create the parser with new. Parse strings with parse. That's all there is to it.

Constants

FORMATS
Racc_arg
Racc_debug_parser
Racc_token_to_s_table

Attributes

format[R]
merror[RW]

If true, Ritex will output a <merror>…</merror> message in the MathML if an unknown entity is encountered. If false (the default), Ritex will throw a Ritex::Error.

Public Class Methods

new(format = :mathml) click to toggle source

format is the desired output format and must be in the FORMATS list. Right now that's just :mathml.

# File lib/ritex.rb, line 39
def initialize format = :mathml
  self.format = format
  @macros = {}
  @merror = false
end

Public Instance Methods

_reduce_none(val, _values, result) click to toggle source
# File lib/ritex/parser.rb, line 1181
def _reduce_none(val, _values, result)
  val[0]
end
flush_macros() click to toggle source

Delete all macros

# File lib/ritex.rb, line 74
def flush_macros; @macros = {}; end
format=(format) click to toggle source
# File lib/ritex.rb, line 68
def format= format
  raise ArgumentError, "format must be one of #{FORMATS * ', '}" unless FORMATS.include? format
  @format = format
end
handle_mathml_markup(what, tag, opts) click to toggle source
# File lib/ritex.rb, line 83
def handle_mathml_markup what, tag, opts
  tag, opts = case tag
    when String
      [tag, opts]
    when Symbol
      a, b = MathML::MARKUP[tag]
      [a, [b, opts].flatten.compact.join(" ")]
    end
  unless opts.empty?
    "<#{tag} #{opts}>#{what}</#{tag}>"
  else
    "<#{tag}>#{what}</#{tag}>"
  end
end
op(o, opts=[]) click to toggle source
# File lib/ritex.rb, line 128
def op o, opts=[]
  case @format
  when :mathml; markup(token(o), "mo", opts)
  when :raw; o
  end
end
parse(s, opts={}) click to toggle source

Parse a string. Returns the MathML output in string form. Note that macro definitions are cumulative and persistent across calls to parse. If you don't want this behavior, you must explicitly call flush_macros after every parse call.

opts is a hash of options:

nowrap, if true, will omit wrapping the output in a top-level XML math tag. Only useful if you're generating these tags yourself.

display, if true, emits display markup, as opposed to inline markup. For mathml output this only has an effect if nowrap is true.

# File lib/ritex.rb, line 57
def parse s, opts={}
  nowrap = opts[:nowrap]
  display = opts[:display]
  @lex = Lexer.new self, s
  r = yyparse @lex, :lex
  r = markup r, (display ? :displaymath : :math) unless nowrap
  r = raw_blob_to_string(r) if @format == :raw
  r
end

Private Instance Methods

define(sym, arity, exp) click to toggle source
# File lib/ritex.rb, line 243
  def define sym, arity, exp
    arity = arity.to_i
    raise Error, "macro arity must be <= 3" unless arity <= 3
    raise Error, "macro arity must be >= 0" unless arity >= 0

#    puts "defining macro #{sym} with exp #{exp} (arity #{arity})"
    warn "overriding definition for #{sym}" if @macros.member? sym
    @macros[sym] = lambda do |*a|
      raise Error, "expecting #{arity} arguments, got #{a.length}" unless a.length == arity
      if @format == :raw
        a = a.map { |x| raw_blob_to_string x }
        exp = raw_blob_to_string(exp)
      end
      (0 ... arity).inject(exp) { |s, i| s.gsub(/\##{i + 1}/, a[i]) }
    end
    @macros[sym].instance_eval "def arity; #{arity}; end" # hack!
    ""
  end
error(e) click to toggle source
# File lib/ritex.rb, line 159
def error e
  if @merror
    "<merror>e</merror>"
  else
    raise Error, e
  end
end
join(*a) click to toggle source
# File lib/ritex.rb, line 174
def join *a
  case @format
  when :mathml; a.join
  when :raw # horrible hack for raw "blobs"
    if a.size == 1
      a[0]
    elsif a.size == 2 && a.first == ""
      a[1]
    else
      a.flatten
    end
  end    
end
raw_funarg(f) click to toggle source
# File lib/ritex.rb, line 214
def raw_funarg f
  f = raw_blob_to_string f
  f[0, 1] == '{' ? f : "{#{f}}"
end
safe(s) click to toggle source
# File lib/ritex.rb, line 167
def safe s
  case @format
  when :mathml; s.gsub("&", "&amp;").gsub(">", "&gt;").gsub("<", "&lt;")
  when :raw; s
  end
end
special(name, *a) click to toggle source
# File lib/ritex.rb, line 188
  def special name, *a
    if @macros.member? name
#puts "evaluating macro (arity #{@macros[name].arity}): type #{name.inspect}, #{a.length} args #{a.inspect}"
      res = @macros[name][*a]
      res = raw_blob_to_string res if @format == :raw
#puts "got #{res}"
      @lex.push res
      ""
    elsif funcs.member? name
#      puts "*** running func #{name}"
      if @format == :raw
        "\\#{name}" + a.map { |x| raw_funarg(x) }.join
      else
        interpret funcs[name][*a]
      end
    elsif envs.member? name
      if @format == :raw
        "\\#{name}" + a.map { |x| raw_funarg(x) }.join
      else
        interpret envs[name][*a]
      end
    else
      error "unknown function, macro or environment #{name.inspect}"
    end
  end