I have finally finished a Ruby calculator project, which is not based on eval. Instead it parses input char by char. The project is hosted on GitHub.
However, I find a specific part of the program very annoying since whenever I have to add something I have to rewrite a portion of it again.
def trig
case @look
when 'c'
match_all('cos')
case @look
when 'h'
match_all('h(')
value = cosh(calculate)
when '('
match('(')
value = cos(calculate)
else
expected('cos() or cosh()')
end
when 's'
match('s')
if @look == 'q'
match_all('qrt(')
value = sqrt(calculate)
else
match_all('in')
case @look
when 'h'
match_all('h(')
value = sinh(calculate)
when '('
match('(')
value = sin(calculate)
else
expected('sin() or sinh()')
end
end
when 'r'
match_all('root')
base = get_number
match('(')
value = calculate ** (1.0/base)
when 't'
match_all('tan')
case @look
when 'h'
match_all('h(')
value = tanh(calculate)
when '('
match('(')
value = tan(calculate)
else
expected('tan() or tanh()')
end
when 'l'
match('l')
case @look
when 'n'
match_all('n(')
value = log(calculate)
when 'o'
match_all('og')
if digit? @look
base = get_number
elsif @look == "("
base = 10
else
expected("integer or ( ")
end
match('(')
value = log(calculate, base)
else
expected('ln() or log()')
end
when 'e'
match_all('exp(')
value = exp(calculate)
when 'a'
match_all('arc')
case @look
when 'c'
match_all('cos')
case @look
when 'h'
match_all('h(')
value = acosh(calculate)
when '('
match('(')
value = acos(calculate)
else
expected('arccos() or arccosh()')
end
when 's'
match_all('sin')
case @look
when 'h'
match_all('h(')
value = asinh(calculate)
when '('
match('(')
value = asin(calculate)
else
expected('arcsin() or arcsinh()')
end
when 't'
match_all('tan')
case @look
when 'h'
match_all('h(')
value = atanh(calculate)
when '('
match('(')
value = atan(calculate)
else
expected('arctan() or arctanh()')
end
end
else
raise InvalidInput, "unexpected input: \"#{@look}\""
end
match(')')
value
end
The @look
variable will contain the next character. The match_all
function will simply match the input character by character. For example, it would match c
and if it works it continues to match o
and so on, but if it doesn't it will output an error.