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 +7 -0
- data/bin/calculator +5 -0
- data/lib/rpn-calculator/input/parser.rb +57 -0
- data/lib/rpn-calculator/input/validator.rb +18 -0
- data/lib/rpn-calculator/io_interface/abstract.rb +13 -0
- data/lib/rpn-calculator/io_interface/standard.rb +15 -0
- data/lib/rpn-calculator/io_processor.rb +29 -0
- data/lib/rpn-calculator/operation/addition.rb +19 -0
- data/lib/rpn-calculator/operation/base.rb +29 -0
- data/lib/rpn-calculator/operation/division.rb +19 -0
- data/lib/rpn-calculator/operation/multiplication.rb +19 -0
- data/lib/rpn-calculator/operation/subtraction.rb +19 -0
- data/lib/rpn-calculator/operation_processor.rb +43 -0
- data/lib/rpn-calculator/result/operation.rb +29 -0
- data/lib/rpn-calculator/result/parser.rb +26 -0
- data/lib/rpn-calculator/result/processor.rb +16 -0
- data/lib/rpn-calculator/result/validator.rb +25 -0
- data/lib/rpn-calculator/version.rb +3 -0
- data/lib/rpn-calculator.rb +29 -0
- metadata +77 -0
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,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,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,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,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: []
|