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