Skip to content

Instantly share code, notes, and snippets.

@theideasmith
Last active August 29, 2015 14:13
Show Gist options
  • Save theideasmith/1437056fcfbe2dc7e3c4 to your computer and use it in GitHub Desktop.
Save theideasmith/1437056fcfbe2dc7e3c4 to your computer and use it in GitHub Desktop.
A mathematical expression parser(I'm still working on the "evaluator")
class String
def integer?
[ # In descending order of likeliness:
/^[-+]?[1-9]([0-9]*)?$/, # decimal
/^0[0-7]+$/, # octal
/^0x[0-9A-Fa-f]+$/, # hexadecimal
/^0b[01]+$/ # binary
].each do |match_pattern|
return true if self =~ match_pattern
end
return false
end
def float?
pat = /^[-+]?[1-9]([0-9]*)?\.[0-9]+$/
return true if self=~pat
false
end
def to_n
return self.to_f if self.float?
return self.to_i if self.integer?
end
end
@operators = {
:+ => Proc.new { |a,b| a+b },
:- => Proc.new { |a,b| a-b},
:* => Proc.new { |a,b| a*b},
:/ => Proc.new { |a,b| a/b},
:^ => Proc.new { |a,b| a**b},
}
@d = 0
@error = false
#manipulate an array by reference
def scopify expr
last_empty = 0
i = 0
pA = 0
scope = 0
numPow = 0
until i == expr.length-1
to_incr =1
# puts "#{expr} :#{expr[i]}"
case expr[i]
when /[\+\-]/
if numPow>0
expr.insert i, ")"*numPow
numPow=0
to_incr+=numPow+1
end
if i-last_empty >1
expr.insert last_empty, "("
expr.insert i+1, ")"
to_incr+=2
end
last_empty=i+to_incr
when /\^/
numPow+=1
expr.insert i+1, "("
to_incr+=1
when '('
last_empty=i+1 if expr[i+1] !='('
when ')'
last_empty=i+1 if expr[i+1].match /[0-9\+\-]/
else
end
i+=to_incr
end
i=expr.length
unless expr[last_empty] == "(" || expr[last_empty..i].integer? || last_empty==0
expr.insert last_empty, "("
expr.insert expr.length, ")"
end
if numPow>0
expr.insert i, ")"*numPow
numPow=0
end
expr
end
def calc_expr expr, array
until @d == expr.length
c = expr[@d]
case c
when "("
@d += 1
array.push calc_expr(expr, Array.new)
when ")"
@d += 1
return array
when /[\*\/]/
@d +=1
array.push c.to_sym
when /[\+\-\^]/
@d+=1
array.push c.to_sym
when /\./
if expr[@d-1]=~/[0-9]+/
x = array.pop.to_s + c + expr[@d+1]
array.push x.to_n
end
@d+=2
when /[0-9x]/
if expr[@d-1]=~/[0-9\.x]/ && array.count>0
x = array.pop.to_s + c
array.push x.to_n
else array.push c.to_n
end
@d+=1
else
unless @error
@error = true
puts "Problem evaluating expression at index:#{@d}"
puts "Char '#{expr[@d]}' not recognized"
end
return
end
end
return array
end
def solv array
operation = nil
total = nil
while array.length > 0
x = array.shift
total = x unless total
if x.class == :s.class
popped = array.shift
if popped.class == 4.class
total = @operators[x].call total, popped
elsif popped.class == [].class
puts "Array"
total = @operators[x].call total, solv(popped)
end
end
end
return total
end
def calc expr
parsed = calc_expr scopify(expr), Array.new
@d = 0
@error = false
solved = parsed
solved
end
# tests = [
# "1+2*3+4",
# "1+2*(3-(2+(3*4+5)*6))+7",
# "1*2*3*4*5*6*7",
# "437*234+56",
# "1+2*3+(34+34*5",
# "1^8^9^10^11^1212+112310239012^980980809897987987"]
# tests += ["1+2*3"]
# tests.each {|t| solv calc(t)}
solv calc("1+2/5*(34)")
module Math
def self.eval( expr )
expr = expr.dup
go = true
while go
go = false
go = true while expr.sub!(/(-?\d+)\s*([*\/])\s*(-?\d+)/) do
puts expr
m,op,n = $1.to_i, $2, $3.to_i
op=="*" ? m*n : m/n
end
go = true while expr.sub!(/(-?\d+)\s*([+-])\s*(-?\d+)/) do
puts expr
a,op,b = $1.to_i, $2, $3.to_i
op=="+" ? a+b : a-b
end
go = true while expr.gsub!(/\(\s*(-?\d+)\s*\)/,'\1') do
puts expr
end
end
expr.to_i
end
end
tests = {
"1" => 1,
"1+1" => 2,
"1 + 1" => 2,
"1 - 1" => 0,
"-1" => -1,
"1 + -1" => 0,
"1 - -1" => 2,
"2*3+1" => 7,
"1+2*3" => 7,
"(1+2)*3" => 9,
"(2+(3-4) *3 ) * -6 * ( 3--4)" => 42,
"4*6/3*2" => 16
}
tests.each do |expr,expected|
actual = Math.eval expr
# puts [expr.inspect,'=>',actual,'instead of',expected].join(' ')
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment