rpn-calculator 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []