Skip to content
Browse files

A proper stack

  • Loading branch information...
1 parent d40e5d0 commit aa569b8c48ad3f91c98ca56f0bb984f50b495352 @txus committed Jan 9, 2011
View
2 lib/rpncalc/calculator.rb
@@ -8,7 +8,7 @@ def initialize options = {}
@parser = Parser.new(delimiter)
end
def solve string
- stack.handle parser.parse(string)
+ stack.solve parser.parse(string)
end
end
end
View
2 lib/rpncalc/parser.rb
@@ -3,7 +3,7 @@ class Parser
class MalformedStringError < StandardError; end;
class InvalidDelimiterError < StandardError; end;
- TOKENS = %w{. + - * / ^ ln log10}
+ TOKENS = %w{. + - * / ^}
attr_reader :delimiter
View
39 lib/rpncalc/stack.rb
@@ -1,4 +1,43 @@
module Rpncalc
class Stack
+ class InsufficientValuesAvailable < StandardError; end;
+ class UnsolvableExpressionError < StandardError; end;
+
+ attr_reader :arity, :elements
+
+ def initialize arity
+ @arity = arity
+ @elements = []
+ end
+
+ def solve tokens
+ tokens.each do |token|
+ if Numeric === token
+ push token
+ next
+ end
+ raise InsufficientValuesAvailable
+ .new("Cannot apply #{token} to less than #{arity} values!") if size < arity
+ result = pop(arity).inject do |acc, e|
+ acc.send(token, e)
+ end
+ push result
+ end
+ raise UnsolvableExpressionError.new("The final stack contained more than one value: #{elements.inspect}") if size > 1
+ elements.first
+ end
+
+ def push token
+ elements.push token
+ end
+
+ def pop amount
+ elements.pop amount
+ end
+
+ def size
+ elements.size
+ end
+
end
end
View
4 spec/rpncalc/calculator_spec.rb
@@ -33,13 +33,13 @@ module Rpncalc
describe "#solve" do
it 'passes the string to the parser' do
- subject.stack.stub(:handle)
+ subject.stack.stub(:solve)
subject.parser.should_receive(:parse).with "3 4 +"
subject.solve "3 4 +"
end
it 'delegates solving to the stack' do
subject.parser.stub(:parse).and_return ["3", "4", "+"]
- subject.stack.should_receive(:handle).with ["3", "4", "+"]
+ subject.stack.should_receive(:solve).with ["3", "4", "+"]
subject.solve "3 4 +"
end
end
View
6 spec/rpncalc/parser_spec.rb
@@ -8,7 +8,7 @@ module Rpncalc
parser.delimiter.should == ' '
end
describe "makes the parser raise an InvalidDelimiterError" do
- %w{. + - * / ^ ln log10 19 425 0}.each do |invalid_delimiter|
+ %w{. + - * / ^ 19 425 0}.each do |invalid_delimiter|
it "when using #{invalid_delimiter} as a delimiter" do
expect {
Parser.new invalid_delimiter
@@ -24,7 +24,7 @@ module Rpncalc
it 'returns a tokenized array' do
subject.parse("1 4 5 + -").should == [1, 4, 5, :+, :-]
- subject.parse("1 2 3 4 5 6 7 8 + - * / ^ ln log10").should == [1, 2, 3, 4, 5, 6, 7, 8, :+, :-, :*, :/, :^, :ln, :log10]
+ subject.parse("1 2 3 4 5 6 7 8 + - * / ^").should == [1, 2, 3, 4, 5, 6, 7, 8, :+, :-, :*, :/, :^]
end
describe "edge cases" do
it 'strips any extra spaces' do
@@ -37,7 +37,7 @@ module Rpncalc
it 'returns a tokenized array' do
subject.parse("1,4,5.3,+,-").should == [1, 4, 5.3, :+, :-]
- subject.parse("1,2,3,4,5,6,7,8,+,-,*,/,^,ln,log10").should == [1,2,3,4,5,6,7,8,:+,:-,:*,:/,:^,:ln,:log10]
+ subject.parse("1,2,3,4,5,6,7,8,+,-,*,/,^").should == [1,2,3,4,5,6,7,8,:+,:-,:*,:/,:^]
end
describe "edge cases" do
it 'strips any extra spaces and delimiter' do
View
80 spec/rpncalc/stack_spec.rb
@@ -0,0 +1,80 @@
+require 'spec_helper'
+
+module Rpncalc
+ describe Stack do
+ subject { Stack.new 2 }
+
+ describe "#initialize" do
+ it 'sets the delimiter' do
+ stack = Stack.new 3
+ stack.arity.should == 3
+ end
+ end
+
+ describe "#solve", "iterates the tokens" do
+ context 'when the token is a value' do
+ it 'pushes it to the stack' do
+ subject.should_receive(:push).with 3
+ subject.solve [3]
+ end
+ end
+ context 'when the token is an operator' do
+ context 'if the size of the stack is below the arity' do
+ it 'raises an error' do
+ subject.stub(:size).and_return 1
+ expect {
+ subject.solve [:+]
+ }.to raise_error(Stack::InsufficientValuesAvailable)
+ end
+ end
+ context 'otherwise' do
+ it 'pops n elements from the stack and pushes the result' do
+ subject.stub(:size).and_return 3, 1
+ elements = [1,2]
+ subject.should_receive(:pop).with(subject.arity).and_return elements
+ subject.should_receive(:push).with(3)
+
+ subject.solve [:+]
+ end
+ end
+ end
+ context 'when at the end there is more than one result' do
+ it 'raises an error' do
+ subject.stub(:size).and_return 2
+ expect {
+ subject.solve [:+]
+ }.to raise_error(Stack::UnsolvableExpressionError)
+ end
+ end
+ context 'when everything is fine' do
+ it 'returns the result' do
+ subject.stub(:size).and_return 3, 1
+ elements = [1,2]
+ subject.stub(:pop).with(subject.arity).and_return elements
+
+ subject.solve([:+]).should == 3
+ end
+ end
+ end
+
+ describe "#push" do
+ it 'pushes a token to the elements collection' do
+ subject.elements.should_receive(:push).with 3
+ subject.push 3
+ end
+ end
+ describe "#pop" do
+ it 'pushes a token to the elements collection' do
+ subject.elements.should_receive(:pop).with 2
+ subject.pop 2
+ end
+ end
+ describe "#size" do
+ it 'pushes a token to the elements collection' do
+ subject.elements.should_receive(:size)
+ subject.size
+ end
+ end
+
+ end
+end

0 comments on commit aa569b8

Please sign in to comment.
Something went wrong with that request. Please try again.