# Algebraic Parser
#
# by Shin-ichiro Hara
#
# Version 1.1 (2001.04.08)
require "algebra/numeric-supplement"
class AlgebraicParser
def self.eval(str, klass)#, coeff = Numeric)
new(str, klass).compile
end
Var = '[a-zA-Z]\d*'
Num = '\d+(?:\.\d*)?'
Op = '(?:\*\*|[+\-*\/^])'
Gr = '[\(\)]'
Dlm = ';'
def initialize(str, klass)
@klass = klass
@coeff = klass.ground
@s = str.gsub(/\s+/, '').scan(/#{Dlm}|#{Num}|#{Op}|#{Gr}|#{Var}|.+/o)
@basis = [] # stack for the basis of the power
end
def compile; sentence; end
private
def head; @s[0]; end
def succ(x = nil)
raise "Error: receive #{x} expect #{head}" if x && x != head
@s.shift
end
def sentence
e = expr
while /^#{Dlm}$/ =~ head
succ(";")
e = expr
end
e
end
def expr
e = expr1
while /^[+-]$/ =~ head
case succ
when "+"
e += expr1
when "-"
e -= expr1
end
end
e
end
def expr1
case head
when "+"; succ; term
when "-"; succ; - term
else term
end
end
def term
e = sequence
while /^[*\/]$/ =~ head #/
case succ
when "*"
e = e * sequence
when "/"
e /= sequence
end
end
e
end
def sequence
e = power
while head && head !~ /^#{Dlm}$|^#{Op}$|^#{Gr}$/o
e *= power
end
e
end
def power
@basis.push(k = factor)
n = 0
while /^(?:\*\*)$|^\^$/ =~ head
succ
@basis.push(factor)
n += 1
end
f = @basis.pop
while n > 0
f = (@basis.pop) ** f
n -= 1
end
f
end
def factor
case head
when "("; succ; e = sentence; succ(")"); e
when /^#{Num}$/o
@basis.empty? ? @coeff.indeterminate(succ) : eval(succ)
when /^#{Var}$/o; @klass.indeterminate(succ)
else
raise "unknown token '#{head}'"
end
end
end
if $0 == __FILE__
class A
def self.indeterminate(str)
case str
when "x"; 7
when "y"; 11
when /\d+/; eval($&)
else
raise
end
end
def self.ground
Fixnum
end
end
p AlgebraicParser.eval("x * y - x^2 + x/8", A) #=> 7*11 - 7**2 + 7/8 = 28
require "algebra/rational"
class B < A
def self.indeterminate(str)
case str
when /\d+/; Rational(eval($&))
else
super
end
end
end
puts AlgebraicParser.eval("x * y - x^2 + x/8", B)
#=> 7*11 - 7**2 + 7/8 = 231/8
p AlgebraicParser.eval("2^(-8+4-2+(3-1)^3)^2", Integer) #=> 16
require "algebra/m-polynomial"
F = MPolynomial(Rational)
p AlgebraicParser.eval("- (2*y)**3 + x", F) #=> -8y^3 + x
F.variables.clear
p AlgebraicParser.eval("x; y; - (2*y)**3 + x", F) #=> x - 8y^3
end
syntax highlighted by Code2HTML, v. 0.9.1