Class: Calculator::Evaluator

Inherits:
Object
  • Object
show all
Defined in:
lib/linmeric/Calculator.rb

Overview

Evaluator parses and evaluates at the same time the stream of tokens

Author

Massimiliano Dal Mas ([email protected])

License

Distributed under MIT license

Constant Summary collapse

OPERATORS =
{
  "+" => lambda do |a,b|
           return (a || 0) + (b || 0)
         end,
  "-" => lambda do |a,b|
           return (a || 0) - (b || 0)
         end,
  "*" => lambda do |a,b|
           return a * b
         end,
  "/" => lambda do |a,b|
           unless b != 0
             @error = true 
             return nil
           end
           return a / b.to_f
         end,
  "^" => lambda do |a,b|
           unless a != 0 and b != 0
             @error = true
             return nil
           end
           return a ** b
         end
}

Instance Method Summary collapse

Instance Method Details

#current_tkObject

  • returns: token of the current pointer value



208
209
210
# File 'lib/linmeric/Calculator.rb', line 208

def current_tk
  return @stream[@i]
end

#evaluate(stream) ⇒ Object

Evaluates the global expression

  • argument: stream of tokens (Array)

  • returns: result of the operations; nil if an error occourred



121
122
123
124
125
126
127
# File 'lib/linmeric/Calculator.rb', line 121

def evaluate(stream) 
  @stack  = []
  @stream = stream 
  @i      = 0
  @error  = false
  return parse 
end

#make_opObject

Solves the operations saved in @op



198
199
200
201
202
203
204
205
# File 'lib/linmeric/Calculator.rb', line 198

def make_op
  while @op.size > 0 and !@error do
    b = @num.pop
    a = @num.pop
    op = @op.pop
    @num.push OPERATORS[op][a,b]
  end
end

#parse(m_end = nil) ⇒ Object

It parses the stream of tokens

  • argument: specific end-token (nil default)

  • returns: result of the operations; nil if an error occourred



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/linmeric/Calculator.rb', line 133

def parse(m_end = nil) 
  @num    = []
  @op     = []
  st = state0_1(0)
  return nil if @error
  @i += 1
  while @i < @stream.size and current_tk.value != m_end do
    st = self.send(st)
    return nil if @error
    @i += 1
  end
  return nil if st == :state_0_1
  make_op
  return @num.pop
end

#priority(op) ⇒ Object

Returns the operator priority

  • argument: operator (string)

  • returns: priority (fixnum)



216
217
218
219
220
221
222
223
224
225
# File 'lib/linmeric/Calculator.rb', line 216

def priority(op)
  case op
    when "+","-"
      return 1
    when "*","/"
      return 2
    when /\^/
      return 3
  end
end

#state0_1(state = 1) ⇒ Object

State0_1 accepts only numbers or ‘(’

  • argument: specification of which state must be runned (1 default)

  • returns: next state (symbol)



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/linmeric/Calculator.rb', line 153

def state0_1(state = 1)
  case current_tk.tag
    when :NUMBER
      @num.push current_tk.value
    when :L_PAR
      @i += 1
      @stack.push @op
      @stack.push @num
      res = parse(")")
      @num = @stack.pop
      @num << res
      @op = @stack.pop
    when :OPERATOR
      if (["+","-"].include? current_tk.value) and (state)== 0 then
        @op.push current_tk.value
        return :state0_1
      else
        @error = true
      end
    else
      @error = true
  end
  return :state2
end

#state2Object

State2 accepts only operators

  • returns: next state (symbol)



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/linmeric/Calculator.rb', line 181

def state2
  if current_tk.tag == :OPERATOR then
    if @op.size == 0 then
      @op.push current_tk.value
    elsif priority(current_tk.value) >= priority(@op.last)
      @op.push current_tk.value
    elsif priority(current_tk.value) < priority(@op.last)
      make_op
      @op.push current_tk.value
    end
  else
    @error = true
  end
  return :state0_1
end