reverse-polish-calculator 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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