calculator-on-ruby 1.0.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/README.md +18 -0
- data/bin/calculator +7 -0
- data/lib/main.rb +220 -0
- data/lib/operators.rb +285 -0
- metadata +75 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 2d709e1d63066165d3a0cf0131adeac870c52ad1992b74209db9f4b9889a687c
|
|
4
|
+
data.tar.gz: 38331de1844359a2aa86135888326e6e5afe049f2872b250a747d7a9433314f3
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c44172f1060712941a9f0127939b9f3c0fd7d643cefb8096643c2df866d1348142f88b3a173ba73309c926186cf6ad473bf1e2f3ee5d9e7d039ac2b4fdbcfe79
|
|
7
|
+
data.tar.gz: ea9807b4d2c09c7b6acb1e0735abb5a2b3ad84b5ff7715cbfc066e71921a2643b7b41a42a3ae93f7d8e45c5c548ecc0de4216cc90be043aba738e0f5cb168c40
|
data/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Calculator on Ruby
|
|
2
|
+
|
|
3
|
+
I started this project so I could learn how to code in ruby. Now I know! But was it worth 500(exact) lines?? Probably not. Oh well.
|
|
4
|
+
|
|
5
|
+
# Usage
|
|
6
|
+
|
|
7
|
+
- Install ruby
|
|
8
|
+
- Windows: `winget install -e --id RubyInstallerTeam.Ruby.4.0` or on ruby's website
|
|
9
|
+
- Fedora: `sudo dnf install ruby`
|
|
10
|
+
- Debian: `sudo apt install ruby-full`
|
|
11
|
+
- Mac: idk, either with brew or on their website
|
|
12
|
+
- Clone this project (`git clone https://gitlab.com/peanutplays61/calculator-on-ruby.git`)
|
|
13
|
+
- run `bundle config set --local without 'development'`
|
|
14
|
+
- run `bundle install`
|
|
15
|
+
- run `bundle exec ruby main.rb`
|
|
16
|
+
- Enter some calculations! There might be some errors at the beginning - this is beyond my control. It only happened on Windows for me.
|
|
17
|
+
|
|
18
|
+
Thanks for trying my project! This unfortunately took me 15 hours.
|
data/bin/calculator
ADDED
data/lib/main.rb
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'sorbet-runtime'
|
|
5
|
+
require 'colorize'
|
|
6
|
+
|
|
7
|
+
class Module
|
|
8
|
+
include T::Sig
|
|
9
|
+
include T::Helpers
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
require_relative 'operators'
|
|
13
|
+
|
|
14
|
+
extend T::Sig
|
|
15
|
+
|
|
16
|
+
class InvalidOperation < StandardError; end
|
|
17
|
+
|
|
18
|
+
sig do
|
|
19
|
+
params(operation: T.nilable(OperatorClass), number: T.nilable(Numeric), total_operation: T.nilable(Operator),
|
|
20
|
+
parentheses_index: Integer).returns(T.nilable(Operator))
|
|
21
|
+
end
|
|
22
|
+
def perform_operation(operation, number, total_operation, parentheses_index)
|
|
23
|
+
make_parentheses_setter = proc { |parentheses|
|
|
24
|
+
proc { |new_value|
|
|
25
|
+
T.let(parentheses, Parentheses).value = T.cast(new_value, Operator)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if total_operation.nil?
|
|
29
|
+
total_operation = if operation.nil?
|
|
30
|
+
Number.new(T.must(number)) unless number.nil?
|
|
31
|
+
elsif operation != Parentheses
|
|
32
|
+
raise InvalidOperation, 'Too many operators in a row'
|
|
33
|
+
else
|
|
34
|
+
Parentheses.new(number.nil? ? nil : Number.new(number))
|
|
35
|
+
end
|
|
36
|
+
else
|
|
37
|
+
raise StandardError, 'Operation is nil, but total operation is not! Impossible!' if operation.nil?
|
|
38
|
+
|
|
39
|
+
current_operation = T.let(total_operation, T.nilable(Operator))
|
|
40
|
+
set_current_operation = proc { |new_value| total_operation = T.cast(new_value, Operator) }
|
|
41
|
+
dive_operation = T.let(current_operation, T.nilable(Operator))
|
|
42
|
+
current_parentheses = (operation == Parentheses ? 1 : 0)
|
|
43
|
+
while current_parentheses < parentheses_index
|
|
44
|
+
if dive_operation.instance_of? Parentheses
|
|
45
|
+
set_current_operation = make_parentheses_setter.call(dive_operation)
|
|
46
|
+
current_operation = dive_operation.value
|
|
47
|
+
|
|
48
|
+
dive_operation = dive_operation.value
|
|
49
|
+
current_parentheses += 1
|
|
50
|
+
elsif dive_operation.instance_of?(Number)
|
|
51
|
+
# is a number, there is an issue
|
|
52
|
+
raise InvalidOperation
|
|
53
|
+
elsif dive_operation.is_a?(TwoSidedOperator)
|
|
54
|
+
dive_operation = dive_operation.right
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
if operation.include? TwoSidedOperator
|
|
59
|
+
operation = T.cast(operation, T.all(T::Class[TwoSidedOperator], Operator::ClassMethods))
|
|
60
|
+
raise InvalidOperation, 'Number must be first in parentheses!' if current_operation.nil?
|
|
61
|
+
|
|
62
|
+
if current_operation.priority >= operation.priority
|
|
63
|
+
set_current_operation.call(operation.new(current_operation, number ? Number.new(number) : nil))
|
|
64
|
+
else
|
|
65
|
+
dive_operation = T.cast(current_operation, TwoSidedOperator)
|
|
66
|
+
|
|
67
|
+
loop do
|
|
68
|
+
if T.must(dive_operation.right).priority > operation.priority
|
|
69
|
+
dive_operation.right = operation.new(dive_operation.right, number ? Number.new(number) : nil)
|
|
70
|
+
break
|
|
71
|
+
end
|
|
72
|
+
dive_operation = dive_operation.right
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
elsif operation == Parentheses
|
|
76
|
+
if current_operation.nil?
|
|
77
|
+
set_current_operation.call(Parentheses.new(number.nil? ? nil : Number.new(number)))
|
|
78
|
+
else
|
|
79
|
+
|
|
80
|
+
dive_operation = T.cast(current_operation, OneOrTwoOperator)
|
|
81
|
+
|
|
82
|
+
get_value = T.let(Kernel.proc do |operator|
|
|
83
|
+
case operator
|
|
84
|
+
when TwoSidedOperator
|
|
85
|
+
operator.right
|
|
86
|
+
when Number
|
|
87
|
+
raise 'Do not call this function with a number type.'
|
|
88
|
+
when Parentheses
|
|
89
|
+
operator.value
|
|
90
|
+
end
|
|
91
|
+
end, T.proc.params(operator: OneOrTwoOperator).returns(T.nilable(Operator)))
|
|
92
|
+
|
|
93
|
+
set_value = T.let(Kernel.proc do |operator, new_value|
|
|
94
|
+
case operator
|
|
95
|
+
when TwoSidedOperator
|
|
96
|
+
operator.right = new_value
|
|
97
|
+
when Number
|
|
98
|
+
raise 'Do not call this function with a number type.'
|
|
99
|
+
when Parentheses
|
|
100
|
+
operator.value = new_value
|
|
101
|
+
end
|
|
102
|
+
end, T.proc.params(operator: OneOrTwoOperator, new_value: OneOrTwoOperator).void)
|
|
103
|
+
|
|
104
|
+
loop do
|
|
105
|
+
if dive_operation.is_a?(Number)
|
|
106
|
+
raise 'This should not happen!'
|
|
107
|
+
elsif get_value.call(dive_operation).nil?
|
|
108
|
+
set_value.call(dive_operation, Parentheses.new(number.nil? ? nil : Number.new(number)))
|
|
109
|
+
break
|
|
110
|
+
elsif T.must(get_value.call(dive_operation)).priority >= Times.priority
|
|
111
|
+
new_dive = get_value.call(dive_operation)
|
|
112
|
+
loop do
|
|
113
|
+
break if new_dive.is_a? Number
|
|
114
|
+
|
|
115
|
+
next_value = get_value.call(T.cast(new_dive, OneOrTwoOperator))
|
|
116
|
+
|
|
117
|
+
if next_value.nil?
|
|
118
|
+
dive_operation = new_dive
|
|
119
|
+
break
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
new_dive = next_value
|
|
123
|
+
end
|
|
124
|
+
if get_value.call(T.cast(new_dive, OneOrTwoOperator)).nil?
|
|
125
|
+
# there is a nil deeper down
|
|
126
|
+
next
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
dive_operation = Times.new(get_value.call(T.cast(dive_operation, OneOrTwoOperator)),
|
|
130
|
+
Parentheses.new(number.nil? ? nil : Number.new(number)))
|
|
131
|
+
break
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
dive_operation = get_value.call(dive_operation)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
total_operation
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def main
|
|
144
|
+
Signal.trap('INT') do
|
|
145
|
+
puts
|
|
146
|
+
return
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
print 'hi! This is a calculator. '
|
|
150
|
+
loop do
|
|
151
|
+
print 'What is your equation? '
|
|
152
|
+
STDOUT.flush # rubocop:disable Style/GlobalStdStream
|
|
153
|
+
|
|
154
|
+
equation = gets.chomp
|
|
155
|
+
current_number = T.let(nil, T.nilable(Numeric))
|
|
156
|
+
total_operation = T.let(nil, T.nilable(Operator))
|
|
157
|
+
current_operation = T.let(nil, T.nilable(OperatorClass))
|
|
158
|
+
number_is_negative = T.let(false, T::Boolean)
|
|
159
|
+
in_decimal_count = 0
|
|
160
|
+
|
|
161
|
+
parentheses_index = 0
|
|
162
|
+
next_parentheses_index = 0
|
|
163
|
+
|
|
164
|
+
begin
|
|
165
|
+
equation.each_char do |char|
|
|
166
|
+
if char.match?(/\d/)
|
|
167
|
+
current_number = if current_number.nil?
|
|
168
|
+
(char.to_i * (number_is_negative ? -1 : 1))
|
|
169
|
+
.fdiv(in_decimal_count.positive? ? T.cast(10.pow(in_decimal_count), Integer) : 1)
|
|
170
|
+
else
|
|
171
|
+
(current_number * (in_decimal_count.positive? ? 1 : 10)) +
|
|
172
|
+
(char.to_i * (number_is_negative ? -1 : 1))
|
|
173
|
+
.fdiv(in_decimal_count.positive? ? T.cast(10.pow(in_decimal_count), Integer) : 1)
|
|
174
|
+
end
|
|
175
|
+
in_decimal_count += 1 if in_decimal_count.positive?
|
|
176
|
+
elsif char == '-' && current_number.nil?
|
|
177
|
+
number_is_negative = true
|
|
178
|
+
elsif char == '.'
|
|
179
|
+
in_decimal_count = 1 if in_decimal_count.zero?
|
|
180
|
+
elsif CHARACTERS.include?(char)
|
|
181
|
+
in_decimal_count = 0
|
|
182
|
+
number_is_negative = false
|
|
183
|
+
|
|
184
|
+
total_operation = perform_operation(current_operation, current_number, total_operation, parentheses_index)
|
|
185
|
+
parentheses_index = next_parentheses_index
|
|
186
|
+
current_operation = CHARACTERS[char]
|
|
187
|
+
current_number = nil
|
|
188
|
+
elsif char == '('
|
|
189
|
+
next_parentheses_index += 1
|
|
190
|
+
total_operation = perform_operation(current_operation, current_number, total_operation, parentheses_index)
|
|
191
|
+
parentheses_index = next_parentheses_index
|
|
192
|
+
current_operation = Parentheses
|
|
193
|
+
elsif char == ')'
|
|
194
|
+
next_parentheses_index -= 1
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
raise InvalidOperation, 'No closing parenthesis!' if next_parentheses_index.positive?
|
|
199
|
+
raise InvalidOperation, 'Too many closing parentheses!' if next_parentheses_index.negative?
|
|
200
|
+
|
|
201
|
+
total_operation = perform_operation(current_operation, current_number, total_operation, parentheses_index)
|
|
202
|
+
raise InvalidOperation, 'No equation' if total_operation.nil?
|
|
203
|
+
|
|
204
|
+
raise InvalidOperation unless total_operation.can_evaluate?
|
|
205
|
+
|
|
206
|
+
puts total_operation.evaluate.round(8)
|
|
207
|
+
rescue InvalidOperation => e
|
|
208
|
+
puts 'That equation is invalid!'.colorize(:red)
|
|
209
|
+
puts "Reason: #{e.message}" if e.message != e.class.to_s
|
|
210
|
+
end
|
|
211
|
+
print 'Would you like to calculate another equation [Y/n]? '
|
|
212
|
+
STDOUT.flush # rubocop:disable Style/GlobalStdStream
|
|
213
|
+
answer = gets.strip.downcase
|
|
214
|
+
unless answer in ('y' | 'yes' | 'yeah' | 'ye' | 'sure' | 'yes.' | '')
|
|
215
|
+
break
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
main if T.let($PROGRAM_NAME, String) == __FILE__
|
data/lib/operators.rb
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
extend T::Sig
|
|
5
|
+
|
|
6
|
+
class Module
|
|
7
|
+
include T::Sig
|
|
8
|
+
include T::Helpers
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# OperatorClass is courtesy of deepseek
|
|
12
|
+
OperatorClass = T.type_alias { T.all(T::Class[Operator], Operator::ClassMethods) }
|
|
13
|
+
OneOrTwoOperator = T.type_alias { T.any(OneSidedOperator, TwoSidedOperator) }
|
|
14
|
+
|
|
15
|
+
module Operator
|
|
16
|
+
abstract!
|
|
17
|
+
include Kernel
|
|
18
|
+
|
|
19
|
+
module ClassMethods
|
|
20
|
+
interface!
|
|
21
|
+
|
|
22
|
+
sig { abstract.returns(Integer) }
|
|
23
|
+
def priority; end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
mixes_in_class_methods(ClassMethods)
|
|
27
|
+
|
|
28
|
+
sig { returns(Integer) }
|
|
29
|
+
def priority
|
|
30
|
+
T.let(self.class, OperatorClass).priority
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
sig { abstract.returns(T::Boolean) }
|
|
34
|
+
def can_evaluate?; end
|
|
35
|
+
|
|
36
|
+
sig { abstract.returns(Numeric) }
|
|
37
|
+
def evaluate; end
|
|
38
|
+
|
|
39
|
+
sig { abstract.returns(String) }
|
|
40
|
+
def to_string; end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
module TwoSidedOperator
|
|
44
|
+
include Operator
|
|
45
|
+
include Kernel
|
|
46
|
+
|
|
47
|
+
abstract!
|
|
48
|
+
|
|
49
|
+
mixes_in_class_methods Operator::ClassMethods
|
|
50
|
+
|
|
51
|
+
sig { returns(T.nilable(Operator)) }
|
|
52
|
+
def left = T.let(@left, T.nilable(Operator))
|
|
53
|
+
|
|
54
|
+
sig { params(left: T.nilable(Operator)).void }
|
|
55
|
+
def left=(left)
|
|
56
|
+
@left = left
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
sig { returns(T.nilable(Operator)) }
|
|
60
|
+
def right = T.let(@right, T.nilable(Operator))
|
|
61
|
+
|
|
62
|
+
sig { params(right: T.nilable(Operator)).void }
|
|
63
|
+
def right=(right)
|
|
64
|
+
@right = right
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
sig { params(left: T.nilable(Operator), right: T.nilable(Operator)).void }
|
|
68
|
+
def initialize(left, right)
|
|
69
|
+
@left = left
|
|
70
|
+
@right = right
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
sig { override.returns(String) }
|
|
74
|
+
def to_string
|
|
75
|
+
"(#{@left.nil? ? 'nil' : @left.to_string}" \
|
|
76
|
+
"#{" #{char}" if is_a?(CharacterOperator)} #{@right.nil? ? 'nil' : @right.to_string})"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
sig { override.returns(T::Boolean) }
|
|
80
|
+
def can_evaluate?
|
|
81
|
+
!(@left.nil? || @right.nil?) && @left.can_evaluate? && @right.can_evaluate?
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
module OneSidedOperator
|
|
86
|
+
include Operator
|
|
87
|
+
|
|
88
|
+
abstract!
|
|
89
|
+
|
|
90
|
+
sig { returns(T.any(T.nilable(Operator), Numeric)) }
|
|
91
|
+
def value = T.let(@value, T.any(T.nilable(Operator), Numeric))
|
|
92
|
+
|
|
93
|
+
sig { params(value: T.any(T.nilable(Operator), Numeric)).void }
|
|
94
|
+
def value=(value)
|
|
95
|
+
@value = value
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
mixes_in_class_methods(Operator::ClassMethods)
|
|
99
|
+
|
|
100
|
+
sig { params(value: T.any(T.nilable(Operator), Numeric)).void }
|
|
101
|
+
def initialize(value); end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
module CharacterOperator
|
|
105
|
+
abstract!
|
|
106
|
+
include Kernel
|
|
107
|
+
|
|
108
|
+
module ClassMethods
|
|
109
|
+
interface!
|
|
110
|
+
include Operator::ClassMethods
|
|
111
|
+
|
|
112
|
+
sig { abstract.returns(String) }
|
|
113
|
+
def char; end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
sig { returns(String) }
|
|
117
|
+
def char
|
|
118
|
+
T.let(self.class, T.all(T::Class[CharacterOperator], ClassMethods)).char
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
mixes_in_class_methods ClassMethods
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
class Plus
|
|
125
|
+
include CharacterOperator
|
|
126
|
+
include TwoSidedOperator
|
|
127
|
+
|
|
128
|
+
sig { override.returns(String) }
|
|
129
|
+
def self.char = '+'
|
|
130
|
+
sig { override.returns(Integer) }
|
|
131
|
+
def self.priority
|
|
132
|
+
0
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
sig { override.returns(Numeric) }
|
|
136
|
+
def evaluate
|
|
137
|
+
T.must_because(@left) { 'Must not be null when evaluating' }.evaluate + T.must_because(@right) do
|
|
138
|
+
'Must not be null when evaluating'
|
|
139
|
+
end.evaluate
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
class Minus
|
|
144
|
+
include TwoSidedOperator
|
|
145
|
+
include CharacterOperator
|
|
146
|
+
|
|
147
|
+
sig { override.returns(String) }
|
|
148
|
+
def self.char = '-'
|
|
149
|
+
sig { override.returns(Integer) }
|
|
150
|
+
def self.priority = 0
|
|
151
|
+
sig { override.returns(Numeric) }
|
|
152
|
+
def evaluate
|
|
153
|
+
T.must_because(@left) { 'Must not be null when evaluating' }.evaluate - T.must_because(@right) {
|
|
154
|
+
'Must not be null when evaluating'
|
|
155
|
+
}.evaluate
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
class Times
|
|
160
|
+
include TwoSidedOperator
|
|
161
|
+
include CharacterOperator
|
|
162
|
+
|
|
163
|
+
sig { override.returns(String) }
|
|
164
|
+
def self.char = '*'
|
|
165
|
+
sig { override.returns(Integer) }
|
|
166
|
+
def self.priority = 1
|
|
167
|
+
sig { override.returns(Numeric) }
|
|
168
|
+
def evaluate
|
|
169
|
+
T.must_because(@left) { 'Must not be null when evaluating' }.evaluate * T.must_because(@right) {
|
|
170
|
+
'Must not be null when evaluating'
|
|
171
|
+
}.evaluate
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
class Divided
|
|
176
|
+
include TwoSidedOperator
|
|
177
|
+
include CharacterOperator
|
|
178
|
+
|
|
179
|
+
sig { override.returns(String) }
|
|
180
|
+
def self.char = '/'
|
|
181
|
+
sig { override.returns(Integer) }
|
|
182
|
+
def self.priority = 1
|
|
183
|
+
sig { override.returns(Numeric) }
|
|
184
|
+
def evaluate
|
|
185
|
+
T.must_because(@left) { 'Must not be null when evaluating' }.evaluate / T.must_because(@right) {
|
|
186
|
+
'Must not be null when evaluating'
|
|
187
|
+
}.evaluate
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
class Power
|
|
192
|
+
include TwoSidedOperator
|
|
193
|
+
include CharacterOperator
|
|
194
|
+
|
|
195
|
+
sig { override.returns(String) }
|
|
196
|
+
def self.char = '^'
|
|
197
|
+
sig { override.returns(Integer) }
|
|
198
|
+
def self.priority = 2
|
|
199
|
+
sig { override.returns(Numeric) }
|
|
200
|
+
def evaluate
|
|
201
|
+
T.cast(T.must_because(@left) do
|
|
202
|
+
'Must not be null when evaluating'
|
|
203
|
+
end.evaluate, T.any(Integer, Float))**T.must_because(@right) {
|
|
204
|
+
'Must not be null when evaluating'
|
|
205
|
+
}.evaluate
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
class Parentheses
|
|
210
|
+
include OneSidedOperator
|
|
211
|
+
|
|
212
|
+
sig { returns T.nilable(Operator) }
|
|
213
|
+
attr_accessor :value
|
|
214
|
+
|
|
215
|
+
sig { override.returns(Integer) }
|
|
216
|
+
def self.priority = 100
|
|
217
|
+
|
|
218
|
+
sig { params(value: T.nilable(Operator)).void }
|
|
219
|
+
def initialize(value)
|
|
220
|
+
@value = value
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
sig { override.returns(Numeric) }
|
|
224
|
+
def evaluate
|
|
225
|
+
T.must_because(@value) { 'Must not be null when evaluating' }.evaluate
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
sig { override.returns(String) }
|
|
229
|
+
def to_string
|
|
230
|
+
"[#{@value&.to_string}]"
|
|
231
|
+
end
|
|
232
|
+
sig { override.returns(T::Boolean) }
|
|
233
|
+
def can_evaluate? = !@value.nil? && @value.can_evaluate?
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
class Number
|
|
237
|
+
include OneSidedOperator
|
|
238
|
+
|
|
239
|
+
sig { override.returns(Integer) }
|
|
240
|
+
def self.priority = 100
|
|
241
|
+
|
|
242
|
+
sig { params(value: Numeric).void }
|
|
243
|
+
def initialize(value)
|
|
244
|
+
@value = value
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
sig { override.returns(Numeric) }
|
|
248
|
+
def evaluate
|
|
249
|
+
@value
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
sig { override.returns(String) }
|
|
253
|
+
def to_string
|
|
254
|
+
"(#{@value})"
|
|
255
|
+
end
|
|
256
|
+
sig { override.returns(T::Boolean) }
|
|
257
|
+
def can_evaluate? = true
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
OPERATORS = T.let(
|
|
261
|
+
[Plus, Minus, Times, Divided, Power, Number].freeze,
|
|
262
|
+
T::Array[OperatorClass]
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
PRIORITIES = T.let(
|
|
266
|
+
OPERATORS.group_by(&:priority),
|
|
267
|
+
T::Hash[Integer, T::Array[OperatorClass]]
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
CHARACTERS = T.let(
|
|
271
|
+
begin
|
|
272
|
+
hash = T::Hash[String, OperatorClass].new
|
|
273
|
+
OPERATORS.each do |operator|
|
|
274
|
+
next unless operator.respond_to?(:char)
|
|
275
|
+
|
|
276
|
+
char_operator = T.cast(
|
|
277
|
+
operator,
|
|
278
|
+
T.all(T::Class[CharacterOperator], CharacterOperator::ClassMethods)
|
|
279
|
+
)
|
|
280
|
+
hash[char_operator.char] = operator
|
|
281
|
+
end
|
|
282
|
+
hash
|
|
283
|
+
end,
|
|
284
|
+
T::Hash[String, OperatorClass]
|
|
285
|
+
)
|
metadata
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: calculator-on-ruby
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Isaiah
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: colorize
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.1'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.1'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: sorbet-runtime
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 0.6.13572
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 0.6.13572
|
|
40
|
+
description: This is an executable script that can run any calculations in string
|
|
41
|
+
form.
|
|
42
|
+
email:
|
|
43
|
+
- peanutplays61@gmail.com
|
|
44
|
+
executables:
|
|
45
|
+
- calculator
|
|
46
|
+
extensions: []
|
|
47
|
+
extra_rdoc_files: []
|
|
48
|
+
files:
|
|
49
|
+
- README.md
|
|
50
|
+
- bin/calculator
|
|
51
|
+
- lib/main.rb
|
|
52
|
+
- lib/operators.rb
|
|
53
|
+
homepage: https://gitlab.com/peanutplays61/calculator-on-ruby
|
|
54
|
+
licenses:
|
|
55
|
+
- MIT
|
|
56
|
+
metadata:
|
|
57
|
+
rubygems_mfa_required: 'true'
|
|
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: 3.4.0
|
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
67
|
+
requirements:
|
|
68
|
+
- - ">="
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '0'
|
|
71
|
+
requirements: []
|
|
72
|
+
rubygems_version: 3.6.9
|
|
73
|
+
specification_version: 4
|
|
74
|
+
summary: A calculator.
|
|
75
|
+
test_files: []
|