Last active
August 29, 2015 14:13
-
-
Save theideasmith/1437056fcfbe2dc7e3c4 to your computer and use it in GitHub Desktop.
A mathematical expression parser(I'm still working on the "evaluator")
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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