rpn_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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 69e3d2c8b29a4f1d5a9cc8c54a8d42815824fb31
4
+ data.tar.gz: d8edf0405654f05e63d698d726cc1174022e83e7
5
+ SHA512:
6
+ metadata.gz: b8a95f45579910a09930e30bafdf1f696a97cba4347c7a095d4336108ef02bdd8693c6fcbad52ba18e3d80b0c6a9721df9247e887d5f7bef84facf4795f2addf
7
+ data.tar.gz: 6e36f41eb613782f243bbe068e5b61eee66250633e0e94e1b6241fb781faae2cf42d5195dc758696a3ce30313c58962ddd5b8f703af5a7e720f11451d64eae76
data/.gitignore ADDED
@@ -0,0 +1,52 @@
1
+ # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2
+ #
3
+ # If you find yourself ignoring temporary files generated by your text editor
4
+ # or operating system, you probably want to add a global ignore instead:
5
+ # git config --global core.excludesfile '~/.gitignore_global'
6
+ # Ignore all test uploads
7
+ /public/uploads/*
8
+ # Ignore bundler config.
9
+ /.bundle
10
+
11
+ # Ignore the default SQLite database.
12
+ /db/*.sqlite3
13
+ /db/*.sqlite3-journal
14
+
15
+ # Ignore all logfiles and tempfiles.
16
+ /log/*.log
17
+ /tmp
18
+ .transcon.log
19
+
20
+ .DS_Store
21
+ Thumbs.db
22
+ .*.swp
23
+ .*.swo
24
+ .bundle
25
+ *~
26
+ \#*
27
+ .\#*
28
+ .redcar
29
+ *.rbc
30
+ doc-pak
31
+ description-pak
32
+
33
+
34
+ *.sassc
35
+ .sass-cache
36
+ capybara-*.html
37
+ .rspec
38
+ /public/system
39
+ /coverage/
40
+ /spec/tmp
41
+ **.orig
42
+ rerun.txt
43
+ pickle-email-*.html
44
+
45
+ ## Environment normalisation:
46
+ /vendor/bundle
47
+
48
+ # these should all be checked in to normalise the environment:
49
+ # Gemfile.lock, .ruby-version, .ruby-gemset
50
+
51
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
52
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rpn_calculator.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,30 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rpn_calculator (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ansi (1.5.0)
10
+ awesome_print (1.6.1)
11
+ builder (3.2.2)
12
+ minitest (5.6.1)
13
+ minitest-reporters (1.0.14)
14
+ ansi
15
+ builder
16
+ minitest (>= 5.0)
17
+ ruby-progressbar
18
+ rake (10.4.2)
19
+ ruby-progressbar (1.7.5)
20
+
21
+ PLATFORMS
22
+ ruby
23
+
24
+ DEPENDENCIES
25
+ awesome_print
26
+ bundler (~> 1.7)
27
+ minitest
28
+ minitest-reporters (>= 1.0.1)
29
+ rake (~> 10.0)
30
+ rpn_calculator!
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Chris Moody
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # RpnCalculator
2
+
3
+ Simple equation parser with a reverse polish notation calculator as a backend
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'rpn_calculator'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install rpn_calculator
20
+
21
+ ## Usage
22
+
23
+ To use as an equation parser and evaluator:
24
+
25
+ ```ruby
26
+ $ RpnCalculator.calculate('1+7-3*16/2**(1+1)') #=> -4
27
+ ```
28
+
29
+ To directly access the reverse polish calculator:
30
+
31
+ ```ruby
32
+ $ RpnCalculator.evaluate([[1,7,3,4,5,6],['+','-','*','/','**']]) #=> 4096
33
+ ```
34
+ ## Contributing
35
+
36
+ 1. Fork it ( https://github.com/[my-github-username]/rpn_calculator/fork )
37
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
38
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
39
+ 4. Push to the branch (`git push origin my-new-feature`)
40
+ 5. Create a new Pull Request
41
+
42
+
43
+ Author
44
+ -------
45
+
46
+ * Chris Moody
47
+
48
+ License
49
+ -------
50
+
51
+ This is free software released into the public domain.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ t.pattern = "test/*_test.rb"
7
+ end
@@ -0,0 +1,7 @@
1
+ require "rpn_calculator/version"
2
+ require "rpn_calculator/rpn_parser"
3
+ require "rpn_calculator/rpn"
4
+ module RpnCalculator
5
+ def self.calculate(args) RPN.calculate(args) end
6
+ def self.evaluate(args) RPN.evaluate(args) end
7
+ end
@@ -0,0 +1,29 @@
1
+ module RpnCalculator
2
+ class RPN
3
+ def initialize(numbers,operators)
4
+ @numbers = numbers
5
+ @operators = operators
6
+ raise ArgumentError, 'A minimum of two numbers is required' if numbers.count < 2
7
+ raise ArgumentError, 'The number of operators must be exactly one less than number of numbers' unless (numbers.count - 1) == operators.count
8
+ raise ArgumentError, 'Only valid numbers allowed' unless are_numbers?
9
+ raise ArgumentError, 'Only valid numbers allowed' unless are_operators?
10
+ end
11
+ attr_accessor :numbers, :operators
12
+
13
+ def self.evaluate(args) new(args[0],args[1]).evaluate end
14
+ def self.calculate(str) RPNParser.parse(str) end
15
+ def evaluate
16
+ evaluated = numbers.shift
17
+ while numbers.count > 0
18
+ evaluated = Float(evaluated).send(operators.shift,Float(numbers.shift))
19
+ end
20
+ return evaluated
21
+ end
22
+
23
+ private
24
+ def floats?(input) !!Float(input) rescue(false) end
25
+ def are_numbers?() numbers.map{|n| floats?(n)}.inject(:&) end
26
+ def are_operators?() operators.map{|op| operator?(op)}.inject(:&) end
27
+ def operator?(input) ['+','-','/','*','**'].include?(input) end
28
+ end
29
+ end
@@ -0,0 +1,58 @@
1
+ module RpnCalculator
2
+ class RPNParser
3
+ def initialize(string)
4
+ raise ArgumentError, 'only numbers and operators allowed as arguments' unless /^[0-9\+\-\*\/\s\.\(\)]+$/ === string
5
+ @string = adjust_string(string)
6
+ end
7
+ def adjust_string(str)
8
+ str.gsub!(/(?<=[0-9])(?=[\+\-\*\/])/,' ')
9
+ str.gsub!(/(?<=[\+\-\*\/])(?=[0-9])/,' ')
10
+ str.gsub(/\s+/,' ')
11
+ end
12
+
13
+ def self.parse(str) self.new(str).parse end
14
+ def return_check() @current == Float(@current) rescue(false) end
15
+ def parse
16
+ @current = mult_div
17
+ return @current if self.return_check
18
+ evaluated = @current.split(' ')
19
+ numbers = [evaluated.shift]
20
+ operators = []
21
+ while evaluated.count > 0
22
+ operators << evaluated.shift
23
+ numbers << evaluated.shift
24
+ end
25
+ RPN.evaluate([numbers,operators])
26
+ end
27
+ def mult_div
28
+ @current = exponent
29
+ return @current if self.return_check
30
+ left = @current.split(/(?<=[0-9\s])(?=[\*\/])/)
31
+ return left.pop if left.count == 1
32
+ right = left.pop.split(' ')
33
+ left = left.join.split(' ')
34
+ evaluated = "#{left.pop} #{right.shift} #{right.shift}"
35
+ return evaluated if left.count + right.count == 0
36
+ RPNParser.parse("#{left.join(' ')}#{RPNParser.parse(evaluated)}#{right.join(' ')}")
37
+ end
38
+ def exponent
39
+ @current = parenthesis
40
+ return @current if self.return_check
41
+ left = @current.split('**')
42
+ return left.pop if left.count == 1
43
+ right = left.pop
44
+ left = left.join('**').split(' ')
45
+ exp = "#{left.pop} ** #{right}"
46
+ return exp if left.count == 0
47
+ RPNParser.parse("#{left.join(' ')} #{RPNParser.parse(exp)}")
48
+ end
49
+ def parenthesis
50
+ left = @string.split('(')
51
+ return left.pop if left.count == 1
52
+ right = left.pop.split(')')
53
+ middle = right.shift
54
+ parens = [left.join('('),middle,right.join(')')]
55
+ RPNParser.parse("#{parens[0]} #{RPNParser.parse(parens[1])} #{parens[2]}")
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,3 @@
1
+ module RpnCalculator
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rpn_calculator/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rpn_calculator"
8
+ spec.version = RpnCalculator::VERSION
9
+ spec.authors = ["Chris Moody"]
10
+ spec.email = ["cmoody@transcon.com"]
11
+ spec.summary = %q{Simple equation parser with a reverse polish notation calculator as a backend}
12
+ # spec.description = %q{TODO: Write a longer description. Optional.}
13
+ spec.homepage = "https://github.com/transcon/rpn_calculator"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency 'minitest'
24
+ spec.add_development_dependency 'awesome_print'
25
+ spec.add_development_dependency 'minitest-reporters', '>= 1.0.1'
26
+ end
@@ -0,0 +1,62 @@
1
+ require 'minitest/autorun'
2
+ require 'rpn_calculator'
3
+ require 'minitest/reporters'
4
+ Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
5
+
6
+ class RpnCalculator::RPNTest < MiniTest::Unit::TestCase
7
+
8
+ def test_should_correctly_evaluate_two_arrays
9
+ numbers = [1,2,5,7,4,3]
10
+ operators = ['+','*','-','/','**']
11
+ assert_equal 8, RpnCalculator::RPN.evaluate([numbers,operators])
12
+ end
13
+
14
+ def test_should_raise_error_if_array_sizes_dont_match
15
+ numbers = [1,2,3]
16
+ operators = ['-']
17
+ assert_raises(ArgumentError) {RpnCalculator::RPN.evaluate([numbers,operators])}
18
+ operators = ['-','+','+']
19
+ assert_raises(ArgumentError) {RpnCalculator::RPN.evaluate([numbers,operators])}
20
+ end
21
+
22
+ def test_should_raise_argument_error_if_not_enough_numbers
23
+ numbers = [1]
24
+ operators = []
25
+ assert_raises(ArgumentError) {RpnCalculator::RPN.evaluate([numbers,operators])}
26
+ end
27
+
28
+ def test_should_raise_error_if_numbers_arent_numbers
29
+ numbers = [1,2,5,7,'a']
30
+ operators = ['+','*','-','/']
31
+ assert_raises(ArgumentError) {RpnCalculator::RPN.evaluate([numbers,operators])}
32
+ end
33
+ def test_should_raise_error_if_operators_arent_operators
34
+ numbers = [1,2,5,7,'8']
35
+ operators = ['+','*','-','g']
36
+ assert_raises(ArgumentError) {RpnCalculator::RPN.evaluate([numbers,operators])}
37
+ end
38
+
39
+ def test_should_calculate_string
40
+ assert_equal 3.0, RpnCalculator::RPN.calculate('1+2')
41
+ end
42
+
43
+ def test_should_observe_order_of_operartions
44
+ assert_equal 9.0, RpnCalculator::RPN.calculate('1+2**3')
45
+ assert_equal 7.0, RpnCalculator::RPN.calculate('1+2*3')
46
+ assert_equal 18.0, RpnCalculator::RPN.calculate('1+2*3+4/2+3**(1+1)')
47
+ end
48
+ def test_should_raise_argument_error
49
+ assert_raises(ArgumentError) {RpnCalculator::RPN.calculate('1+one')}
50
+ end
51
+
52
+ def test_rpn_calculator_calculate_wrapper
53
+ assert_equal 9.0, RpnCalculator.calculate('1+2**3')
54
+ assert_equal 7.0, RpnCalculator.calculate('1+2*3')
55
+ assert_equal 18.0, RpnCalculator.calculate('1+2*3+4/2+3**(1+1)')
56
+ end
57
+ def test_rpn_calculator_evaluate_wrapper
58
+ numbers = [1,2,5,7,4,3]
59
+ operators = ['+','*','-','/','**']
60
+ assert_equal 8, RpnCalculator.evaluate([numbers,operators])
61
+ end
62
+ end
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rpn_calculator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Chris Moody
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: awesome_print
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest-reporters
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 1.0.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 1.0.1
83
+ description:
84
+ email:
85
+ - cmoody@transcon.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - Gemfile.lock
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - lib/rpn_calculator.rb
97
+ - lib/rpn_calculator/rpn.rb
98
+ - lib/rpn_calculator/rpn_parser.rb
99
+ - lib/rpn_calculator/version.rb
100
+ - rpn_calculator.gemspec
101
+ - test/rpn_calculator_test.rb
102
+ homepage: https://github.com/transcon/rpn_calculator
103
+ licenses:
104
+ - MIT
105
+ metadata: {}
106
+ post_install_message:
107
+ rdoc_options: []
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ requirements: []
121
+ rubyforge_project:
122
+ rubygems_version: 2.4.3
123
+ signing_key:
124
+ specification_version: 4
125
+ summary: Simple equation parser with a reverse polish notation calculator as a backend
126
+ test_files:
127
+ - test/rpn_calculator_test.rb