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
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
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
# File lib/ritex/parser.rb, line 1181 def _reduce_none(val, _values, result) val[0] end
Delete all macros
# File lib/ritex.rb, line 74 def flush_macros; @macros = {}; end
# File lib/ritex.rb, line 68 def format= format raise ArgumentError, "format must be one of #{FORMATS * ', '}" unless FORMATS.include? format @format = format end
# 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
# 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 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
# 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
# File lib/ritex.rb, line 159 def error e if @merror "<merror>e</merror>" else raise Error, e end end
# 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
# File lib/ritex.rb, line 214 def raw_funarg f f = raw_blob_to_string f f[0, 1] == '{' ? f : "{#{f}}" end
# File lib/ritex.rb, line 167 def safe s case @format when :mathml; s.gsub("&", "&").gsub(">", ">").gsub("<", "<") when :raw; s end end
# 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