rpn-calculator 0.1.0

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: 63cc0de9b3942ee248de5064c5085419751f6276
4
+ data.tar.gz: cee536cb6a8babe8603382a12830d7e2793283ca
5
+ SHA512:
6
+ metadata.gz: f6a86512842f45dab14b521cb701f3dc2d2d288f5d2a03dbd779611e6e12fbd20ce6d62b3acb9fc0ece527a01f9873d4647b532b7cbce2e18a4d3c89b8798048
7
+ data.tar.gz: 80affa700d36acd2a6fd0782c94a86d0d6a412e39199a6392e4b653141c27e2663e5d50fe6cd132bcc3750c0d5c45b2d18bacf469dbe9a5100161059ee92b61f
data/bin/calculator ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rpn-calculator'
4
+
5
+ RPNCalculator.start
@@ -0,0 +1,57 @@
1
+ module RPNCalculator
2
+ module Input
3
+ class Parser
4
+ def initialize(allowed_operators)
5
+ @allowed_operators = allowed_operators
6
+ end
7
+
8
+ def parse(input_string)
9
+ parsed_input = input_without_whitespace(
10
+ join_consecutive_numbers(input_string.split(''))
11
+ )
12
+ invalid_elements = parsed_input_errors(parsed_input)
13
+ Result::Parser.new(parsed_input, invalid_elements)
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :allowed_operators
19
+
20
+ def input_without_whitespace(split_string)
21
+ split_string.select { |e| e!= ' ' }
22
+ end
23
+
24
+ def parsed_input_errors(parsed_input)
25
+ parsed_input.inject([]) do |result, element|
26
+ result << element unless allowed_operators.include?(element) || is_number?(element)
27
+ result
28
+ end
29
+ end
30
+
31
+ def join_consecutive_numbers(split_string)
32
+ split_string.each_with_index.inject([]) do |result, (element, index)|
33
+ if index == 0 || any_operator?([split_string[index - 1], element])
34
+ result << element
35
+ else
36
+ result[-1] += element
37
+ end
38
+ result
39
+ end
40
+ end
41
+
42
+ def any_operator?(elements)
43
+ elements.any? { |e| !is_number_or_period?(e) }
44
+ end
45
+
46
+ def is_number_or_period?(number_string)
47
+ number_string == '.' || is_number?(number_string)
48
+ end
49
+
50
+ def is_number?(number_string)
51
+ true if Float(number_string)
52
+ rescue ArgumentError
53
+ false
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,18 @@
1
+ module RPNCalculator
2
+ module Input
3
+ class Validator
4
+ def initialize(invalid_arguments_regex)
5
+ @invalid_arguments_regex = invalid_arguments_regex
6
+ end
7
+
8
+ def validate(input_string)
9
+ invalid_characters = input_string.scan(invalid_arguments_regex)
10
+ Result::Validator.new(invalid_characters)
11
+ end
12
+
13
+ private
14
+
15
+ attr_reader :invalid_arguments_regex
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ module RPNCalculator
2
+ module IoInterface
3
+ class Abstract
4
+ def read_input
5
+ raise 'Must implement in subclass'
6
+ end
7
+
8
+ def display_output
9
+ raise 'Must implement in subclass'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module RPNCalculator
2
+ module IoInterface
3
+ class Standard < Abstract
4
+ def read_input
5
+ print '> '
6
+ input = STDIN.gets
7
+ input.chomp unless input.nil? || input == "q\n"
8
+ end
9
+
10
+ def display_output(result)
11
+ puts result
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ module RPNCalculator
2
+ class IoProcessor
3
+ def initialize(io_interface, operation_processor)
4
+ @io_interface = io_interface
5
+ @input_stack = []
6
+ @operation_processor = operation_processor
7
+ end
8
+
9
+ def start
10
+ while (input = io_interface.read_input)
11
+ processor_result = operation_processor.process(input_stack, input)
12
+ if processor_result.valid?
13
+ print_result_array(processor_result.result)
14
+ @input_stack = processor_result.result
15
+ else
16
+ io_interface.display_output(processor_result.error)
17
+ end
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :io_interface, :operation_processor, :input_stack
24
+
25
+ def print_result_array(result)
26
+ io_interface.display_output(result.join(' '))
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ require 'rpn-calculator/operation/base'
2
+
3
+ module RPNCalculator
4
+ module Operation
5
+ class Addition < Base
6
+ def result
7
+ return invalid_operation_result(operation_string) unless valid?
8
+
9
+ Result::Operation.new(operation_string, float_operands.reduce(&:+))
10
+ end
11
+
12
+ private
13
+
14
+ def operation_string
15
+ '+'
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ module RPNCalculator
2
+ module Operation
3
+ class Base
4
+ def initialize(operands)
5
+ @operands = operands
6
+ end
7
+
8
+ def result
9
+ raise 'must implement in subclass'
10
+ end
11
+
12
+ private
13
+
14
+ attr_reader :operands
15
+
16
+ def valid?
17
+ operands.size == 2
18
+ end
19
+
20
+ def float_operands
21
+ operands.map { |operator| Float(operator) }
22
+ end
23
+
24
+ def invalid_operation_result(operation)
25
+ Result::Operation.new(operation, [], operands)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ require 'rpn-calculator/operation/base'
2
+
3
+ module RPNCalculator
4
+ module Operation
5
+ class Division < Base
6
+ def result
7
+ return invalid_operation_result(operation_string) unless valid?
8
+
9
+ Result::Operation.new(operation_string, float_operands.reduce(&:/))
10
+ end
11
+
12
+ private
13
+
14
+ def operation_string
15
+ '/'
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ require 'rpn-calculator/operation/base'
2
+
3
+ module RPNCalculator
4
+ module Operation
5
+ class Multiplication < Base
6
+ def result
7
+ return invalid_operation_result(operation_string) unless valid?
8
+
9
+ Result::Operation.new(operation_string, float_operands.reduce(&:*))
10
+ end
11
+
12
+ private
13
+
14
+ def operation_string
15
+ '*'
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ require 'rpn-calculator/operation/base'
2
+
3
+ module RPNCalculator
4
+ module Operation
5
+ class Subtraction < Base
6
+ def result
7
+ return invalid_operation_result(operation_string) unless valid?
8
+
9
+ Result::Operation.new(operation_string, float_operands.reduce(&:-))
10
+ end
11
+
12
+ private
13
+
14
+ def operation_string
15
+ '-'
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,43 @@
1
+ module RPNCalculator
2
+ class OperationProcessor
3
+ def initialize(operation_classes, input_validator, input_parser)
4
+ @input_validator = input_validator
5
+ @input_parser = input_parser
6
+ @operation_classes = operation_classes
7
+ @operation_symbols = @operation_classes.keys
8
+ end
9
+
10
+ def process(previous_operations, input)
11
+ validator_result = input_validator.validate(input)
12
+ return Result::Processor.new([], validator_result.error) unless validator_result.valid?
13
+
14
+ parser_result = input_parser.parse(input)
15
+ return Result::Processor.new([], parser_result.error) unless parser_result.valid?
16
+ process_operations(previous_operations + parser_result.parsed_elements)
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :input_validator, :input_parser, :operation_classes, :operation_symbols
22
+
23
+ def process_operations(operations)
24
+ operation_stack = []
25
+ while operations.size > 0
26
+ element = operations.shift
27
+ if operation_symbols.include?(element)
28
+ result = operation_classes.fetch(element).new(operation_stack).result
29
+ return invalid_processor_result(result) unless result.valid?
30
+
31
+ operation_stack = [result.result]
32
+ else
33
+ operation_stack.push(element)
34
+ end
35
+ end
36
+ Result::Processor.new(operation_stack)
37
+ end
38
+
39
+ def invalid_processor_result(result)
40
+ Result::Processor.new([], result.error)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,29 @@
1
+ module RPNCalculator
2
+ module Result
3
+ class Operation
4
+ def initialize(operation, result, invalid_operation_elements = [])
5
+ @operation = operation
6
+ @result = result
7
+ @invalid_operation_elements = invalid_operation_elements
8
+ end
9
+
10
+ def valid?
11
+ invalid_operation_elements.empty?
12
+ end
13
+
14
+ def error
15
+ "Invalid operation: #{invalid_operation}" unless valid?
16
+ end
17
+
18
+ attr_reader :result
19
+
20
+ private
21
+
22
+ attr_reader :invalid_operation_elements, :operation
23
+
24
+ def invalid_operation
25
+ invalid_operation_elements.join(' ') + " #{operation}"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,26 @@
1
+ module RPNCalculator
2
+ module Result
3
+ class Parser
4
+ def initialize(parsed_elements = [], invalid_elements = [])
5
+ @parsed_elements = parsed_elements
6
+ @invalid_elements = invalid_elements
7
+ end
8
+
9
+ def valid?
10
+ invalid_elements.empty?
11
+ end
12
+
13
+ def error
14
+ "Invalid operators or numbers: #{invalid_element_list}" unless valid?
15
+ end
16
+
17
+ attr_reader :parsed_elements, :invalid_elements
18
+
19
+ private
20
+
21
+ def invalid_element_list
22
+ invalid_elements.join(', ')
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ module RPNCalculator
2
+ module Result
3
+ class Processor
4
+ def initialize(result = [], error = nil)
5
+ @result = result
6
+ @error = error
7
+ end
8
+
9
+ def valid?
10
+ error.nil?
11
+ end
12
+
13
+ attr_reader :result, :error
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+ module RPNCalculator
2
+ module Result
3
+ class Validator
4
+ def initialize(invalid_characters = [])
5
+ @invalid_characters = invalid_characters
6
+ end
7
+
8
+ def valid?
9
+ invalid_characters.empty?
10
+ end
11
+
12
+ def error
13
+ "Invalid characters: #{invalid_character_list}" unless valid?
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :invalid_characters
19
+
20
+ def invalid_character_list
21
+ invalid_characters.join(', ')
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module RPNCalculator
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -0,0 +1,29 @@
1
+ Dir.glob('lib/**/*.rb').each do |file|
2
+ require file.gsub('lib/', '').gsub('.rb', '')
3
+ end
4
+
5
+ module RPNCalculator
6
+ OPERATION_CLASSES = {
7
+ '+' => Operation::Addition,
8
+ '-' => Operation::Subtraction,
9
+ '*' => Operation::Multiplication,
10
+ '/' => Operation::Division
11
+ }.freeze
12
+ ALLOWED_OPERATORS = OPERATION_CLASSES.keys.freeze
13
+ INVALID_ARGUMENTS_REGEX = /[^\d\s\+\-\/\*\.]/.freeze
14
+
15
+ module_function
16
+
17
+ def start
18
+ operation_processor = OperationProcessor.new(
19
+ OPERATION_CLASSES,
20
+ Input::Validator.new(INVALID_ARGUMENTS_REGEX),
21
+ Input::Parser.new(ALLOWED_OPERATORS)
22
+ )
23
+
24
+ # Here is where we could read and write to another input
25
+ # using stdin and stdout by default
26
+ processor = IoProcessor.new(IoInterface::Standard.new, operation_processor)
27
+ processor.start
28
+ end
29
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rpn-calculator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Mario Celi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-11-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.7'
27
+ description:
28
+ email:
29
+ - mcelicalderon@gmail.com
30
+ executables:
31
+ - calculator
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - bin/calculator
36
+ - lib/rpn-calculator.rb
37
+ - lib/rpn-calculator/input/parser.rb
38
+ - lib/rpn-calculator/input/validator.rb
39
+ - lib/rpn-calculator/io_interface/abstract.rb
40
+ - lib/rpn-calculator/io_interface/standard.rb
41
+ - lib/rpn-calculator/io_processor.rb
42
+ - lib/rpn-calculator/operation/addition.rb
43
+ - lib/rpn-calculator/operation/base.rb
44
+ - lib/rpn-calculator/operation/division.rb
45
+ - lib/rpn-calculator/operation/multiplication.rb
46
+ - lib/rpn-calculator/operation/subtraction.rb
47
+ - lib/rpn-calculator/operation_processor.rb
48
+ - lib/rpn-calculator/result/operation.rb
49
+ - lib/rpn-calculator/result/parser.rb
50
+ - lib/rpn-calculator/result/processor.rb
51
+ - lib/rpn-calculator/result/validator.rb
52
+ - lib/rpn-calculator/version.rb
53
+ homepage: https://github.com/mcelicalderon/rpn-calculator
54
+ licenses:
55
+ - MIT
56
+ metadata: {}
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements: []
72
+ rubyforge_project:
73
+ rubygems_version: 2.6.13
74
+ signing_key:
75
+ specification_version: 4
76
+ summary: Basic RPN Calculator
77
+ test_files: []