reverse-polish-calculator 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .DS_Store
6
+ tmp
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.9.2@reverse-polish-calculator
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Justin Ko
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,28 @@
1
+ # reverse-polish-calculator
2
+
3
+ This is a [RPN](http://en.wikipedia.org/wiki/Reverse_Polish_notation) (Reverse Polish Notation) command line calculator. It supports arithmetic, and *most* of the methods in Ruby's [Math](http://www.ruby-doc.org/core/classes/Math.html) module.
4
+
5
+ ## Synopsis
6
+
7
+ $ rpc
8
+ $ 5 # => stack: 5.0
9
+ aggregate: 0
10
+ $ 5 # => stack: 5.0, 5.0
11
+ aggregate: 0
12
+ $ + # => calculated: 5.0 + 5.0
13
+ stack:
14
+ aggregate: 10.0
15
+
16
+ For more examples, check out the acceptance specs.
17
+
18
+ ## Installation
19
+
20
+ gem install reverse-polish-calculator
21
+
22
+ ## License
23
+
24
+ reverse-polish-calculator is released under the MIT license. See LICENSE for details.
25
+
26
+ ## Copyright
27
+
28
+ Copyright (c) 2011 Justin Ko
@@ -0,0 +1,15 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ task :default => ['spec:unit', 'spec:acceptance']
5
+
6
+ namespace :spec do
7
+
8
+ [:acceptance, :unit].each do |suite|
9
+ desc "Run the #{suite} specs"
10
+ RSpec::Core::RakeTask.new(suite) do |t|
11
+ t.pattern = "./spec/#{suite}/**/*_spec.rb"
12
+ end
13
+ end
14
+
15
+ end
data/bin/rpc ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
3
+
4
+ require 'reverse-polish-calculator'
5
+
6
+ ReversePolishCalculator.start
@@ -0,0 +1,31 @@
1
+ require 'reverse-polish-calculator/helpers'
2
+ require 'reverse-polish-calculator/stack'
3
+ require 'reverse-polish-calculator/inputs'
4
+ require 'reverse-polish-calculator/input'
5
+ require 'reverse-polish-calculator/errors'
6
+ require 'reverse-polish-calculator/output'
7
+
8
+ module ReversePolishCalculator
9
+
10
+ def self.start
11
+ loop_with_error_handling do
12
+ stack.add(gets.chomp)
13
+ stack.calculate
14
+ stack.output
15
+ end
16
+ end
17
+
18
+ def self.stack
19
+ @stack ||= Stack.new
20
+ end
21
+
22
+ def self.loop_with_error_handling
23
+ loop { yield }
24
+ rescue *Errors::Classes => exception
25
+ Errors.handle(exception)
26
+ Output.clear
27
+ stack.unswap
28
+ retry
29
+ end
30
+
31
+ end
@@ -0,0 +1,27 @@
1
+ module ReversePolishCalculator
2
+ module Errors
3
+
4
+ MethodMapping = {
5
+ NoMethodError => :no_method_error,
6
+ Math::DomainError => :math_domain_error
7
+ }
8
+
9
+ Classes = MethodMapping.keys
10
+
11
+ def self.handle(exception)
12
+ send(MethodMapping[exception.class], exception)
13
+ end
14
+
15
+ def self.no_method_error(exception)
16
+ ReversePolishCalculator.stack.remove(exception.name)
17
+ Helpers.multi_puts "Cannot calculate '#{exception.name}'"
18
+ end
19
+
20
+ def self.math_domain_error(exception)
21
+ name = exception.message.match(/"(.+)"/)[1]
22
+ ReversePolishCalculator.stack.remove(name)
23
+ Helpers.multi_puts "Bad value for '#{name}'"
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ module ReversePolishCalculator
2
+ module Helpers
3
+
4
+ def self.multi_puts(*strings)
5
+ return if $in_unit_spec_suite
6
+ strings.each {|str| puts str }
7
+ puts
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,38 @@
1
+ module ReversePolishCalculator
2
+ class Input
3
+
4
+ attr_reader :value
5
+
6
+ def initialize(value)
7
+ @value = value
8
+ exit if exited?
9
+ end
10
+
11
+ def exited?
12
+ value.downcase == 'q'
13
+ end
14
+
15
+ def invoke(input_1, input_2 = nil)
16
+ if math_method?
17
+ Output.add "calculated: #{value}(#{input_1.to_f})"
18
+ Math.send(value, input_1.to_f)
19
+ else
20
+ Output.add "calculated: #{input_1.to_f} #{value} #{input_2.to_f}"
21
+ input_1.to_f.send(value, input_2.to_f)
22
+ end
23
+ end
24
+
25
+ def math_method?
26
+ Math.respond_to?(value)
27
+ end
28
+
29
+ def operand?
30
+ !!value[/\d/]
31
+ end
32
+
33
+ def to_f
34
+ value.to_f
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,79 @@
1
+ module ReversePolishCalculator
2
+ module Inputs
3
+
4
+ attr_accessor :swapped
5
+
6
+ def calculate(aggregate = 0)
7
+ @operands, @trash = [], []
8
+ @aggregate = aggregate
9
+
10
+ each do |input|
11
+ if input.operand?
12
+ add_operand(input)
13
+ elsif input.math_method?
14
+ invoke_math_method(input)
15
+ else
16
+ invoke_binary_operator(input)
17
+ end
18
+ end
19
+
20
+ empty_trash
21
+ calculation
22
+ end
23
+
24
+ def add_operand(input)
25
+ @operands << input
26
+ @calculated = false
27
+ end
28
+
29
+ def invoke_math_method(input)
30
+ operand = @operands.pop || @aggregate
31
+ @operands.push input.invoke(operand)
32
+ @trash << input << operand
33
+ @calculated = true
34
+ end
35
+
36
+ def invoke_binary_operator(input)
37
+ operand_1 = @operands.pop
38
+ operand_2 = @operands.pop
39
+
40
+ unless operand_2
41
+ self.swapped = true
42
+ operand_2 = operand_1
43
+ operand_1 = @aggregate
44
+ else
45
+ self.swapped = false
46
+ end
47
+
48
+ @operands.push(if @swapped
49
+ input.invoke(operand_1, operand_2)
50
+ else
51
+ input.invoke(operand_2, operand_1)
52
+ end)
53
+
54
+ @trash << input << operand_1 << operand_2
55
+ @calculated = true
56
+ end
57
+
58
+ def calculation
59
+ if @calculated
60
+ if swapped
61
+ @operands.first
62
+ else
63
+ @aggregate + @operands.last.to_f
64
+ end
65
+ else
66
+ @aggregate
67
+ end
68
+ end
69
+
70
+ def empty_trash
71
+ delete_if {|input| @trash.include?(input) }
72
+ end
73
+
74
+ def inspect
75
+ map(&:to_f).join(', ')
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,21 @@
1
+ module ReversePolishCalculator
2
+ module Output
3
+
4
+ def self.add(str)
5
+ texts.push(str)
6
+ end
7
+
8
+ def self.texts
9
+ @texts ||= []
10
+ end
11
+
12
+ def self.display
13
+ Helpers.multi_puts(*texts)
14
+ end
15
+
16
+ def self.clear
17
+ texts.clear
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ module ReversePolishCalculator
2
+ class Stack
3
+
4
+ attr_reader :inputs, :aggregate
5
+
6
+ def initialize
7
+ @inputs, @aggregate = [].extend(Inputs), 0
8
+ end
9
+
10
+ def add(expression)
11
+ expression.split.each do |value|
12
+ @inputs << Input.new(value)
13
+ end
14
+ end
15
+
16
+ def remove(input_value)
17
+ @inputs.delete_if do |input|
18
+ input.value == input_value.to_s
19
+ end
20
+ end
21
+
22
+ def calculate
23
+ @aggregate = @inputs.calculate(aggregate)
24
+ end
25
+
26
+ def output
27
+ Output.add "stack: #{@inputs.inspect}"
28
+ Output.add "aggregate: #{@aggregate}"
29
+ Output.display
30
+ Output.clear
31
+ end
32
+
33
+ def unswap
34
+ inputs.swapped = false
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module ReversePolishCalculator
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'reverse-polish-calculator/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'reverse-polish-calculator'
7
+ s.version = ReversePolishCalculator::VERSION
8
+ s.authors = 'Justin Ko'
9
+ s.email = 'jko170@gmail.com'
10
+ s.homepage = 'https://github.com/justinko/reverse-polish-calculator'
11
+ s.summary = 'Reverse Polish Calculator'
12
+ s.description = 'Command line RPN (Reverse Polish Notation) calculator'
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_path = 'lib'
18
+
19
+ s.add_development_dependency 'rake'
20
+ s.add_development_dependency 'rspec'
21
+ s.add_development_dependency 'aruba'
22
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'complex expressions' do
4
+
5
+ describe '5 1 2 + 4 * + 3 - 2 / cos' do
6
+ context 'on one line' do
7
+ it 'aggregates to 7.0' do
8
+ start
9
+ type '5 1 2 + 4 * + 3 - 2 / cos'
10
+ type 'q'
11
+
12
+ assert_exact_output <<-OUTPUT.rpc
13
+ calculated: 1.0 + 2.0
14
+ calculated: 3.0 * 4.0
15
+ calculated: 5.0 + 12.0
16
+ calculated: 17.0 - 3.0
17
+ calculated: 14.0 / 2.0
18
+ calculated: cos(7.0)
19
+ stack:
20
+ aggregate: 0.7539022543433046
21
+
22
+ OUTPUT
23
+ end
24
+ end
25
+
26
+ context 'on separate lines' do
27
+ it 'aggregates to 10.0' do
28
+ start
29
+ type '5'
30
+ type '1'
31
+ type '2'
32
+ type '+'
33
+ type '4'
34
+ type '*'
35
+ type '+'
36
+ type '3'
37
+ type '-'
38
+ type '2'
39
+ type '/'
40
+ type 'cos'
41
+ type 'q'
42
+
43
+ assert_exact_output <<-OUTPUT.rpc
44
+ stack: 5.0
45
+ aggregate: 0
46
+
47
+ stack: 5.0, 1.0
48
+ aggregate: 0
49
+
50
+ stack: 5.0, 1.0, 2.0
51
+ aggregate: 0
52
+
53
+ calculated: 1.0 + 2.0
54
+ stack: 5.0
55
+ aggregate: 3.0
56
+
57
+ stack: 5.0, 4.0
58
+ aggregate: 3.0
59
+
60
+ calculated: 5.0 * 4.0
61
+ stack:
62
+ aggregate: 23.0
63
+
64
+ calculated: 23.0 + 0.0
65
+ stack:
66
+ aggregate: 23.0
67
+
68
+ stack: 3.0
69
+ aggregate: 23.0
70
+
71
+ calculated: 23.0 - 3.0
72
+ stack:
73
+ aggregate: 20.0
74
+
75
+ stack: 2.0
76
+ aggregate: 20.0
77
+
78
+ calculated: 20.0 / 2.0
79
+ stack:
80
+ aggregate: 10.0
81
+
82
+ calculated: cos(10.0)
83
+ stack:
84
+ aggregate: -0.8390715290764524
85
+
86
+ OUTPUT
87
+ end
88
+ end
89
+ end
90
+
91
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'error handling' do
4
+
5
+ describe "for an input that can't be calculated" do
6
+ it 'provides a message' do
7
+ start
8
+ type 'foo'
9
+ type '5'
10
+ type '4'
11
+ type '+'
12
+ type 'q'
13
+
14
+ assert_exact_output <<-OUTPUT.rpc
15
+ Cannot calculate 'foo'
16
+
17
+ stack: 5.0
18
+ aggregate: 0
19
+
20
+ stack: 5.0, 4.0
21
+ aggregate: 0
22
+
23
+ calculated: 5.0 + 4.0
24
+ stack:
25
+ aggregate: 9.0
26
+
27
+ OUTPUT
28
+ end
29
+ end
30
+
31
+ describe 'for a trigonometric method' do
32
+ context 'that is called with a non-computable value' do
33
+ it 'provides a message' do
34
+ start
35
+ type '5'
36
+ type 'acos'
37
+ type 'q'
38
+
39
+ assert_exact_output <<-OUTPUT.rpc
40
+ stack: 5.0
41
+ aggregate: 0
42
+
43
+ Bad value for 'acos'
44
+
45
+ OUTPUT
46
+ end
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,173 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'simple arithmetic' do
4
+
5
+ describe 'addition' do
6
+ describe '2 2 +' do
7
+ context 'on separate lines' do
8
+ it 'aggregates to 4.0' do
9
+ start
10
+ type '2'
11
+ type '2'
12
+ type '+'
13
+ type 'q'
14
+
15
+ assert_exact_output <<-OUTPUT.rpc
16
+ stack: 2.0
17
+ aggregate: 0
18
+
19
+ stack: 2.0, 2.0
20
+ aggregate: 0
21
+
22
+ calculated: 2.0 + 2.0
23
+ stack:
24
+ aggregate: 4.0
25
+
26
+ OUTPUT
27
+ end
28
+ end
29
+
30
+ context 'on one line' do
31
+ it 'aggregates to 4.0' do
32
+ start
33
+ type '2 2 +'
34
+ type 'q'
35
+
36
+ assert_exact_output <<-OUTPUT.rpc
37
+ calculated: 2.0 + 2.0
38
+ stack:
39
+ aggregate: 4.0
40
+
41
+ OUTPUT
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ describe 'subtraction' do
48
+ describe '4 2 -' do
49
+ context 'on seperate lines' do
50
+ it 'aggregates to 2.0' do
51
+ start
52
+ type '4'
53
+ type '2'
54
+ type '-'
55
+ type 'q'
56
+
57
+ assert_exact_output <<-OUTPUT.rpc
58
+ stack: 4.0
59
+ aggregate: 0
60
+
61
+ stack: 4.0, 2.0
62
+ aggregate: 0
63
+
64
+ calculated: 4.0 - 2.0
65
+ stack:
66
+ aggregate: 2.0
67
+
68
+ OUTPUT
69
+ end
70
+ end
71
+
72
+ context 'on one line' do
73
+ it 'aggregates to 2.0' do
74
+ start
75
+ type '4 2 -'
76
+ type 'q'
77
+
78
+ assert_exact_output <<-OUTPUT.rpc
79
+ calculated: 4.0 - 2.0
80
+ stack:
81
+ aggregate: 2.0
82
+
83
+ OUTPUT
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ describe 'multiplication' do
90
+ describe '3 2 *' do
91
+ context 'on seperate lines' do
92
+ it 'aggregates to 6.0' do
93
+ start
94
+ type '3'
95
+ type '2'
96
+ type '*'
97
+ type 'q'
98
+
99
+ assert_exact_output <<-OUTPUT.rpc
100
+ stack: 3.0
101
+ aggregate: 0
102
+
103
+ stack: 3.0, 2.0
104
+ aggregate: 0
105
+
106
+ calculated: 3.0 * 2.0
107
+ stack:
108
+ aggregate: 6.0
109
+
110
+ OUTPUT
111
+ end
112
+ end
113
+
114
+ context 'on one line' do
115
+ it 'aggregates to 6.0' do
116
+ start
117
+ type '3 2 *'
118
+ type 'q'
119
+
120
+ assert_exact_output <<-OUTPUT.rpc
121
+ calculated: 3.0 * 2.0
122
+ stack:
123
+ aggregate: 6.0
124
+
125
+ OUTPUT
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ describe 'division' do
132
+ describe '10 2 /' do
133
+ context 'on seperate lines' do
134
+ it 'aggregates to 5.0' do
135
+ start
136
+ type '10'
137
+ type '2'
138
+ type '/'
139
+ type 'q'
140
+
141
+ assert_exact_output <<-OUTPUT.rpc
142
+ stack: 10.0
143
+ aggregate: 0
144
+
145
+ stack: 10.0, 2.0
146
+ aggregate: 0
147
+
148
+ calculated: 10.0 / 2.0
149
+ stack:
150
+ aggregate: 5.0
151
+
152
+ OUTPUT
153
+ end
154
+ end
155
+
156
+ context 'on one line' do
157
+ it 'aggregates to 5.0' do
158
+ start
159
+ type '10 2 /'
160
+ type 'q'
161
+
162
+ assert_exact_output <<-OUTPUT.rpc
163
+ calculated: 10.0 / 2.0
164
+ stack:
165
+ aggregate: 5.0
166
+
167
+ OUTPUT
168
+ end
169
+ end
170
+ end
171
+ end
172
+
173
+ end
@@ -0,0 +1,89 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'simple trigonometric' do
4
+
5
+ describe 'acos' do
6
+ describe 'given -1' do
7
+ it 'returns 3.141592653589793' do
8
+ start
9
+ type '-1'
10
+ type 'acos'
11
+ type 'q'
12
+
13
+ assert_exact_output <<-OUTPUT.rpc
14
+ stack: -1.0
15
+ aggregate: 0
16
+
17
+ calculated: acos(-1.0)
18
+ stack:
19
+ aggregate: 3.141592653589793
20
+
21
+ OUTPUT
22
+ end
23
+ end
24
+ end
25
+
26
+ describe 'asin' do
27
+ describe 'given -1' do
28
+ it 'returns -1.5707963267948966' do
29
+ start
30
+ type '-1'
31
+ type 'asin'
32
+ type 'q'
33
+
34
+ assert_exact_output <<-OUTPUT.rpc
35
+ stack: -1.0
36
+ aggregate: 0
37
+
38
+ calculated: asin(-1.0)
39
+ stack:
40
+ aggregate: -1.5707963267948966
41
+
42
+ OUTPUT
43
+ end
44
+ end
45
+ end
46
+
47
+ describe 'cos' do
48
+ describe 'given 1' do
49
+ it 'returns 0.5403023058681398' do
50
+ start
51
+ type '1'
52
+ type 'cos'
53
+ type 'q'
54
+
55
+ assert_exact_output <<-OUTPUT.rpc
56
+ stack: 1.0
57
+ aggregate: 0
58
+
59
+ calculated: cos(1.0)
60
+ stack:
61
+ aggregate: 0.5403023058681398
62
+
63
+ OUTPUT
64
+ end
65
+ end
66
+ end
67
+
68
+ describe 'tan' do
69
+ describe 'given 1' do
70
+ it 'returns 1.557407724654902' do
71
+ start
72
+ type '1'
73
+ type 'tan'
74
+ type 'q'
75
+
76
+ assert_exact_output <<-OUTPUT.rpc
77
+ stack: 1.0
78
+ aggregate: 0
79
+
80
+ calculated: tan(1.0)
81
+ stack:
82
+ aggregate: 1.557407724654902
83
+
84
+ OUTPUT
85
+ end
86
+ end
87
+ end
88
+
89
+ end
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup
4
+
5
+ require 'reverse-polish-calculator'
6
+ require 'aruba/api'
7
+
8
+ Dir['./spec/support/*.rb'].map {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+ config.color_enabled = true
12
+ config.include Aruba::Api, :example_group => {
13
+ :file_path => /spec\/acceptance/
14
+ }
15
+ config.before(:suite, :example_group => {
16
+ :file_path => /spec\/unit/
17
+ }) { $in_unit_spec_suite = true }
18
+ end
@@ -0,0 +1,4 @@
1
+ def start
2
+ @aruba_io_wait_seconds = 0.5
3
+ run_interactive './../../bin/rpc'
4
+ end
@@ -0,0 +1,5 @@
1
+ class String
2
+ def rpc
3
+ split("\n").map(&:lstrip).join("\n") << "\n\n"
4
+ end
5
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ module ReversePolishCalculator
4
+ describe Errors do
5
+
6
+ describe '.handle' do
7
+ context 'given a NoMethodError exception' do
8
+ let(:exception) { NoMethodError.new }
9
+
10
+ it 'calls .no_method_error' do
11
+ described_class.should_receive(:no_method_error).with(exception)
12
+ described_class.handle(exception)
13
+ end
14
+ end
15
+ end
16
+
17
+ describe '.no_method_error' do
18
+ context 'given a NoMethodError exception' do
19
+ let(:exception) { NoMethodError.new('message', 'name') }
20
+
21
+ it 'removes the offending input from the stack' do
22
+ ReversePolishCalculator.stack.should_receive(:remove).with('name')
23
+ described_class.no_method_error(exception)
24
+ end
25
+ end
26
+ end
27
+
28
+ describe '.math_domain_error' do
29
+ context 'given a Math::DomainError exception' do
30
+ let(:exception) { Math::DomainError.new('the "name"') }
31
+
32
+ it 'removes the offending input from the stack' do
33
+ ReversePolishCalculator.stack.should_receive(:remove).with('name')
34
+ described_class.math_domain_error(exception)
35
+ end
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+
3
+ module ReversePolishCalculator
4
+ describe Input do
5
+
6
+ describe '#exited?' do
7
+ let(:input) { described_class.allocate }
8
+
9
+ context 'with input value of "q"' do
10
+ it 'returns true' do
11
+ input.instance_variable_set('@value', 'q')
12
+ input.should be_exited
13
+ end
14
+ end
15
+
16
+ context 'with input value of "Q"' do
17
+ it 'returns true' do
18
+ input.instance_variable_set('@value', 'Q')
19
+ input.should be_exited
20
+ end
21
+ end
22
+
23
+ context 'with input value of "foo"' do
24
+ it 'returns false' do
25
+ input.instance_variable_set('@value', 'foo')
26
+ input.should_not be_exited
27
+ end
28
+ end
29
+ end
30
+
31
+ describe '#invoke' do
32
+ context 'with input value of "+"' do
33
+ let(:input) { described_class.new('+') }
34
+
35
+ context 'given 2 inputs that have a value of "2"' do
36
+ let(:input_1) { described_class.new('2') }
37
+ let(:input_2) { described_class.new('2') }
38
+
39
+ it 'returns 4.0' do
40
+ input.invoke(input_1, input_2).should eq(4.0)
41
+ end
42
+ end
43
+ end
44
+
45
+ context 'with input value of "acos"' do
46
+ let(:input) { described_class.new('acos') }
47
+
48
+ context 'given 1 input with a value of "-1"' do
49
+ let(:input_1) { described_class.new('-1') }
50
+
51
+ it 'returns 3.141592653589793' do
52
+ input.invoke(input_1).should eq(3.141592653589793)
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ describe '#math_method?' do
59
+ context 'with input value of "acos"' do
60
+ it 'returns true' do
61
+ described_class.new('acos').should be_math_method
62
+ end
63
+ end
64
+
65
+ context 'with input value of "foo"' do
66
+ it 'returns false' do
67
+ described_class.new('foo').should_not be_math_method
68
+ end
69
+ end
70
+ end
71
+
72
+ describe '#operand?' do
73
+ context 'with input value of "5"' do
74
+ it 'returns true' do
75
+ described_class.new('5').should be_operand
76
+ end
77
+ end
78
+
79
+ context 'with input value of "foo"' do
80
+ it 'returns false' do
81
+ described_class.new('foo').should_not be_operand
82
+ end
83
+ end
84
+ end
85
+
86
+ describe '#to_f' do
87
+ context 'with input value of "5"' do
88
+ it 'returns 5.0' do
89
+ described_class.new('5').to_f.should eq(5.0)
90
+ end
91
+ end
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ module ReversePolishCalculator
4
+ describe Inputs do
5
+ let(:inputs) { [].extend(described_class) }
6
+
7
+ describe '#calculate' do
8
+ context 'given inputs: 2 2 +' do
9
+ before { add_inputs('2 2 +') }
10
+
11
+ it 'returns 4.0' do
12
+ inputs.calculate.should eq(4.0)
13
+ end
14
+ end
15
+
16
+ context 'given inputs: 2 +' do
17
+ before { add_inputs('2 +') }
18
+
19
+ it 'returns 2.0' do
20
+ inputs.calculate.should eq(2.0)
21
+ end
22
+ end
23
+
24
+ context 'given inputs: 2 2 2 + +' do
25
+ before { add_inputs('2 2 2 + +') }
26
+
27
+ it 'returns 6.0' do
28
+ inputs.calculate.should eq(6.0)
29
+ end
30
+ end
31
+
32
+ context 'given inputs: 2 2 + +' do
33
+ before { add_inputs('2 2 + +') }
34
+
35
+ it 'returns 4.0' do
36
+ inputs.calculate.should eq(4.0)
37
+ end
38
+ end
39
+
40
+ context 'given inputs: -1 acos' do
41
+ before { add_inputs('-1 acos') }
42
+
43
+ it 'returns 3.141592653589793' do
44
+ inputs.calculate.should eq(3.141592653589793)
45
+ end
46
+ end
47
+
48
+ context 'given inputs: cos' do
49
+ before { add_inputs('cos') }
50
+
51
+ context 'and an aggregate of 3.0' do
52
+ it 'returns 2.010007503399555' do
53
+ inputs.calculate(3.0).should eq(2.010007503399555)
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ describe '#inspect' do
60
+ before { add_inputs('1 2') }
61
+ specify { inputs.inspect.should eq('1.0, 2.0') }
62
+ end
63
+
64
+ def add_inputs(expression)
65
+ expression.split.each do |input|
66
+ inputs << Input.new(input)
67
+ end
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ module ReversePolishCalculator
4
+ describe Output do
5
+
6
+ describe '.add' do
7
+ context 'given "foo"' do
8
+ it 'adds "foo" to the list of texts' do
9
+ described_class.add('foo')
10
+ described_class.texts.should include('foo')
11
+ end
12
+ end
13
+ end
14
+
15
+ describe '.texts' do
16
+ it 'returns an array' do
17
+ described_class.texts.should be_an_instance_of(Array)
18
+ end
19
+ end
20
+
21
+ describe '.display' do
22
+ before { described_class.clear }
23
+
24
+ it 'calls Helpers.multi_puts with the list of texts' do
25
+ described_class.add('foo')
26
+ Helpers.should_receive(:multi_puts).with('foo')
27
+ described_class.display
28
+ end
29
+ end
30
+
31
+ describe '.clear' do
32
+ it 'clears out the list of texts' do
33
+ described_class.add('foo')
34
+ described_class.clear
35
+ described_class.texts.should be_empty
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+
3
+ module ReversePolishCalculator
4
+ describe Stack do
5
+
6
+ describe '#add' do
7
+ context 'given a single character' do
8
+ before { subject.add('5') }
9
+
10
+ it 'adds one input' do
11
+ subject.should have(1).inputs
12
+ end
13
+ end
14
+
15
+ context 'given 3 characters between spaces' do
16
+ before { subject.add('1 2 3') }
17
+
18
+ it 'adds 3 inputs' do
19
+ subject.should have(3).inputs
20
+ end
21
+ end
22
+ end
23
+
24
+ describe '#remove' do
25
+ context 'given an input value that exists in inputs' do
26
+ before { subject.add('foo') }
27
+
28
+ it 'removes the input' do
29
+ expect do
30
+ subject.remove('foo')
31
+ end.to change(subject.inputs, :size).by(-1)
32
+ end
33
+ end
34
+ end
35
+
36
+ describe '#calculate' do
37
+ it 'aggregates' do
38
+ subject.inputs << Input.new('2')
39
+ subject.inputs << Input.new('2')
40
+ subject.inputs << Input.new('+')
41
+
42
+ subject.calculate
43
+ subject.aggregate.should eq(4.0)
44
+
45
+ subject.inputs << Input.new('2')
46
+ subject.inputs << Input.new('2')
47
+ subject.inputs << Input.new('+')
48
+
49
+ subject.calculate
50
+ subject.aggregate.should eq(8.0)
51
+
52
+ subject.inputs << Input.new('2')
53
+ subject.inputs << Input.new('-')
54
+
55
+ subject.calculate
56
+ subject.aggregate.should eq(6.0)
57
+
58
+ subject.inputs << Input.new('2')
59
+ subject.inputs << Input.new('*')
60
+
61
+ subject.calculate
62
+ subject.aggregate.should eq(12.0)
63
+ end
64
+ end
65
+
66
+ describe '#output' do
67
+ it 'calls .add on Output, twice' do
68
+ Output.should_receive(:add).twice
69
+ subject.output
70
+ end
71
+
72
+ it 'calls .display on Output' do
73
+ Output.should_receive(:display)
74
+ subject.output
75
+ end
76
+
77
+ it 'calls .clear on Output' do
78
+ Output.should_receive(:clear)
79
+ subject.output
80
+ end
81
+ end
82
+
83
+ describe '#unswap' do
84
+ context 'with inputs.swapped set to true' do
85
+ before { subject.inputs.swapped = true }
86
+
87
+ it 'sets inputs.swapped to false' do
88
+ expect { subject.unswap }.to change {
89
+ subject.inputs.swapped }.from(true).to(false)
90
+ end
91
+ end
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe ReversePolishCalculator do
4
+
5
+ describe '.start' do
6
+ it 'starts the loop' do
7
+ described_class.should_receive(:loop)
8
+ described_class.start
9
+ end
10
+ end
11
+
12
+ describe '.stack' do
13
+ it 'returns an instance of Stack' do
14
+ described_class.stack.should be_an_instance_of(described_class::Stack)
15
+ end
16
+ end
17
+
18
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: reverse-polish-calculator
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Justin Ko
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-06-06 00:00:00 -06:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rake
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: "0"
25
+ type: :development
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: "0"
36
+ type: :development
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: aruba
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ type: :development
48
+ version_requirements: *id003
49
+ description: Command line RPN (Reverse Polish Notation) calculator
50
+ email: jko170@gmail.com
51
+ executables:
52
+ - rpc
53
+ extensions: []
54
+
55
+ extra_rdoc_files: []
56
+
57
+ files:
58
+ - .gitignore
59
+ - .rvmrc
60
+ - Gemfile
61
+ - LICENSE
62
+ - README.md
63
+ - Rakefile
64
+ - bin/rpc
65
+ - lib/reverse-polish-calculator.rb
66
+ - lib/reverse-polish-calculator/errors.rb
67
+ - lib/reverse-polish-calculator/helpers.rb
68
+ - lib/reverse-polish-calculator/input.rb
69
+ - lib/reverse-polish-calculator/inputs.rb
70
+ - lib/reverse-polish-calculator/output.rb
71
+ - lib/reverse-polish-calculator/stack.rb
72
+ - lib/reverse-polish-calculator/version.rb
73
+ - reverse-polish-calculator.gemspec
74
+ - spec/acceptance/complex_expressions_spec.rb
75
+ - spec/acceptance/error_handling_spec.rb
76
+ - spec/acceptance/simple_arithmetic_spec.rb
77
+ - spec/acceptance/simple_trigonometric_spec.rb
78
+ - spec/spec_helper.rb
79
+ - spec/support/aruba.rb
80
+ - spec/support/string_ext.rb
81
+ - spec/unit/reverse-polish-calculator/errors_spec.rb
82
+ - spec/unit/reverse-polish-calculator/input_spec.rb
83
+ - spec/unit/reverse-polish-calculator/inputs_spec.rb
84
+ - spec/unit/reverse-polish-calculator/output_spec.rb
85
+ - spec/unit/reverse-polish-calculator/stack_spec.rb
86
+ - spec/unit/reverse-polish-calculator_spec.rb
87
+ has_rdoc: true
88
+ homepage: https://github.com/justinko/reverse-polish-calculator
89
+ licenses: []
90
+
91
+ post_install_message:
92
+ rdoc_options: []
93
+
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: "0"
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: "0"
108
+ requirements: []
109
+
110
+ rubyforge_project:
111
+ rubygems_version: 1.6.2
112
+ signing_key:
113
+ specification_version: 3
114
+ summary: Reverse Polish Calculator
115
+ test_files:
116
+ - spec/acceptance/complex_expressions_spec.rb
117
+ - spec/acceptance/error_handling_spec.rb
118
+ - spec/acceptance/simple_arithmetic_spec.rb
119
+ - spec/acceptance/simple_trigonometric_spec.rb
120
+ - spec/spec_helper.rb
121
+ - spec/support/aruba.rb
122
+ - spec/support/string_ext.rb
123
+ - spec/unit/reverse-polish-calculator/errors_spec.rb
124
+ - spec/unit/reverse-polish-calculator/input_spec.rb
125
+ - spec/unit/reverse-polish-calculator/inputs_spec.rb
126
+ - spec/unit/reverse-polish-calculator/output_spec.rb
127
+ - spec/unit/reverse-polish-calculator/stack_spec.rb
128
+ - spec/unit/reverse-polish-calculator_spec.rb