keisan 0.2.1 → 0.3.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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +92 -0
  3. data/lib/keisan.rb +12 -0
  4. data/lib/keisan/ast/bitwise_and.rb +1 -5
  5. data/lib/keisan/ast/bitwise_or.rb +1 -5
  6. data/lib/keisan/ast/bitwise_xor.rb +1 -5
  7. data/lib/keisan/ast/builder.rb +57 -22
  8. data/lib/keisan/ast/exponent.rb +1 -5
  9. data/lib/keisan/ast/function.rb +50 -1
  10. data/lib/keisan/ast/logical_and.rb +1 -5
  11. data/lib/keisan/ast/logical_equal.rb +18 -0
  12. data/lib/keisan/ast/logical_greater_than.rb +4 -4
  13. data/lib/keisan/ast/logical_greater_than_or_equal_to.rb +4 -4
  14. data/lib/keisan/ast/logical_less_than.rb +4 -4
  15. data/lib/keisan/ast/logical_less_than_or_equal_to.rb +4 -4
  16. data/lib/keisan/ast/logical_not_equal.rb +18 -0
  17. data/lib/keisan/ast/logical_or.rb +1 -5
  18. data/lib/keisan/ast/modulo.rb +18 -0
  19. data/lib/keisan/ast/node.rb +26 -0
  20. data/lib/keisan/ast/operator.rb +9 -2
  21. data/lib/keisan/ast/plus.rb +1 -5
  22. data/lib/keisan/ast/priorities.rb +27 -0
  23. data/lib/keisan/ast/times.rb +1 -5
  24. data/lib/keisan/ast/variable.rb +5 -0
  25. data/lib/keisan/calculator.rb +1 -12
  26. data/lib/keisan/context.rb +20 -5
  27. data/lib/keisan/evaluator.rb +79 -0
  28. data/lib/keisan/exceptions.rb +1 -0
  29. data/lib/keisan/functions/default_registry.rb +0 -5
  30. data/lib/keisan/functions/registry.rb +7 -0
  31. data/lib/keisan/parser.rb +42 -29
  32. data/lib/keisan/parsing/dot.rb +6 -0
  33. data/lib/keisan/parsing/dot_operator.rb +12 -0
  34. data/lib/keisan/parsing/dot_word.rb +14 -0
  35. data/lib/keisan/parsing/logical_equal.rb +9 -0
  36. data/lib/keisan/parsing/logical_not_equal.rb +9 -0
  37. data/lib/keisan/parsing/modulo.rb +9 -0
  38. data/lib/keisan/tokenizer.rb +2 -1
  39. data/lib/keisan/tokens/arithmetic_operator.rb +4 -1
  40. data/lib/keisan/tokens/dot.rb +11 -0
  41. data/lib/keisan/tokens/logical_operator.rb +7 -1
  42. data/lib/keisan/variables/registry.rb +7 -0
  43. data/lib/keisan/version.rb +1 -1
  44. metadata +14 -2
@@ -14,7 +14,6 @@ module Keisan
14
14
 
15
15
  def self.register_defaults!(registry)
16
16
  register_builtin_math!(registry)
17
- register_branch_methods!(registry)
18
17
  register_array_methods!(registry)
19
18
  register_random_methods!(registry)
20
19
  end
@@ -30,10 +29,6 @@ module Keisan
30
29
  end
31
30
  end
32
31
 
33
- def self.register_branch_methods!(registry)
34
- registry.register!(:if, Proc.new {|bool, a, b=nil| bool ? a : b })
35
- end
36
-
37
32
  def self.register_array_methods!(registry)
38
33
  %i(min max size).each do |method|
39
34
  registry.register!(method, Proc.new {|a| a.send(method)})
@@ -18,6 +18,13 @@ module Keisan
18
18
  raise Keisan::Exceptions::UndefinedFunctionError.new name
19
19
  end
20
20
 
21
+ def has?(name)
22
+ !!self[name]
23
+ rescue Keisan::Exceptions::UndefinedFunctionError
24
+ false
25
+ end
26
+
27
+ # For checking if locally defined
21
28
  def has_name?(name)
22
29
  @hash.has_key?(name)
23
30
  end
data/lib/keisan/parser.rb CHANGED
@@ -73,12 +73,35 @@ module Keisan
73
73
  # Here it is a postfix Indexing (access elements by index)
74
74
  elsif token.type == :group && token.group_type == :square
75
75
  add_indexing_to_components!(token)
76
+ elsif token.type == :dot
77
+ @components << Keisan::Parsing::Dot.new
76
78
  else
77
79
  # Expect an operator
78
80
  raise Keisan::Exceptions::ParseError.new("Expected an operator, received #{token.string}") unless token.type == :operator
79
81
  add_operator_to_components!(token)
80
82
  end
81
83
 
84
+ elsif @components[-1].is_a?(Parsing::Dot)
85
+ # Expect a word
86
+ case token.type
87
+ when :word
88
+ @components[-1] = Parsing::DotWord.new(token.string)
89
+ else
90
+ raise Keisan::Exceptions::ParseError.new("A word must follow a dot, received #{token.string}")
91
+ end
92
+
93
+ elsif @components[-1].is_a?(Parsing::DotWord)
94
+ # Expect a round group
95
+ if token.type == :group && token.group_type == :round
96
+ name = @components[-1].name
97
+ @components[-1] = Parsing::DotOperator.new(name, arguments_from_group(token))
98
+ elsif token.type == :dot
99
+ @components << Keisan::Parsing::Dot.new
100
+ elsif token.type == :group && token.group_type == :square
101
+ add_indexing_to_components!(token)
102
+ else
103
+ raise Keisan::Exceptions::ParseError.new("Expected arguments to dot operator, received #{token.string}")
104
+ end
82
105
  else
83
106
  raise Keisan::Exceptions::ParseError.new("Token cannot be parsed, #{token.string}")
84
107
  end
@@ -120,15 +143,7 @@ module Keisan
120
143
  when :round
121
144
  @components << Keisan::Parsing::RoundGroup.new(token.sub_tokens)
122
145
  when :square
123
- @components << if token.sub_tokens.empty?
124
- Parsing::List.new([])
125
- else
126
- Parsing::List.new(
127
- token.sub_tokens.split {|sub_token| sub_token.is_a?(Keisan::Tokens::Comma)}.map do |sub_tokens|
128
- Parsing::Argument.new(sub_tokens)
129
- end
130
- )
131
- end
146
+ @components << Parsing::List.new(arguments_from_group(token))
132
147
  else
133
148
  raise Keisan::Exceptions::ParseError.new("Unhandled group type #{token.group_type}")
134
149
  end
@@ -150,6 +165,8 @@ module Keisan
150
165
  @components << Keisan::Parsing::Divide.new
151
166
  when :**
152
167
  @components << Keisan::Parsing::Exponent.new
168
+ when :%
169
+ @components << Keisan::Parsing::Modulo.new
153
170
  # Bitwise
154
171
  when :"&"
155
172
  @components << Keisan::Parsing::BitwiseAnd.new
@@ -162,6 +179,10 @@ module Keisan
162
179
  when :"~~"
163
180
  @components << Keisan::Parsing::BitwiseNotNot.new
164
181
  # Logical
182
+ when :"=="
183
+ @components << Keisan::Parsing::LogicalEqual.new
184
+ when :"!="
185
+ @components << Keisan::Parsing::LogicalNotEqual.new
165
186
  when :"&&"
166
187
  @components << Keisan::Parsing::LogicalAnd.new
167
188
  when :"||"
@@ -184,30 +205,22 @@ module Keisan
184
205
  end
185
206
 
186
207
  def add_function_to_components!(token)
187
- # Have a function actually, not a variable
188
- if token.sub_tokens.empty?
189
- @components[-1] = Parsing::Function.new(@components[-1].name, [])
190
- else
191
- @components[-1] = Parsing::Function.new(
192
- @components[-1].name,
193
- token.sub_tokens.split {|sub_token| sub_token.is_a?(Keisan::Tokens::Comma)}.map do |sub_tokens|
194
- Parsing::Argument.new(sub_tokens)
195
- end
196
- )
197
- end
208
+ @components[-1] = Parsing::Function.new(@components[-1].name, arguments_from_group(token))
198
209
  end
199
210
 
200
211
  def add_indexing_to_components!(token)
201
212
  # Have an indexing
202
- @components << if token.sub_tokens.empty?
203
- Parsing::Indexing.new([])
204
- else
205
- Parsing::Indexing.new(
206
- token.sub_tokens.split {|sub_token| sub_token.is_a?(Keisan::Tokens::Comma)}.map do |sub_tokens|
207
- Parsing::Argument.new(sub_tokens)
208
- end
209
- )
210
- end
213
+ @components << Parsing::Indexing.new(arguments_from_group(token))
214
+ end
215
+
216
+ def arguments_from_group(token)
217
+ if token.sub_tokens.empty?
218
+ []
219
+ else
220
+ token.sub_tokens.split {|sub_token| sub_token.is_a?(Keisan::Tokens::Comma)}.map do |sub_tokens|
221
+ Parsing::Argument.new(sub_tokens)
222
+ end
223
+ end
211
224
  end
212
225
  end
213
226
  end
@@ -0,0 +1,6 @@
1
+ module Keisan
2
+ module Parsing
3
+ class Dot < Component
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,12 @@
1
+ module Keisan
2
+ module Parsing
3
+ class DotOperator < Element
4
+ attr_reader :name, :arguments
5
+
6
+ def initialize(name, arguments)
7
+ @name = name
8
+ @arguments = arguments
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module Keisan
2
+ module Parsing
3
+ class DotWord < Component
4
+ attr_reader :name
5
+ def initialize(name)
6
+ @name = name
7
+ end
8
+
9
+ def arguments
10
+ []
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ module Keisan
2
+ module Parsing
3
+ class LogicalEqual < LogicalOperator
4
+ def node_class
5
+ Keisan::AST::LogicalEqual
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Keisan
2
+ module Parsing
3
+ class LogicalNotEqual < LogicalOperator
4
+ def node_class
5
+ Keisan::AST::LogicalNotEqual
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Keisan
2
+ module Parsing
3
+ class Modulo < ArithmeticOperator
4
+ def node_class
5
+ Keisan::AST::Modulo
6
+ end
7
+ end
8
+ end
9
+ end
@@ -10,7 +10,8 @@ module Keisan
10
10
  Tokens::ArithmeticOperator,
11
11
  Tokens::LogicalOperator,
12
12
  Tokens::BitwiseOperator,
13
- Tokens::Comma
13
+ Tokens::Comma,
14
+ Tokens::Dot
14
15
  ]
15
16
 
16
17
  TOKEN_REGEX = Regexp::new(
@@ -4,8 +4,9 @@ module Keisan
4
4
  EXPONENT = /(?:\*\*)/
5
5
  TIMES = /(?:\*)/
6
6
  DIVIDE = /(?:\/)/
7
+ MODULO = /(?:\%)/
7
8
  PLUS_OR_MINUS = /(?:[\+\-]+)/
8
- REGEX = /(#{EXPONENT}|#{TIMES}|#{DIVIDE}|#{PLUS_OR_MINUS})/
9
+ REGEX = /(#{EXPONENT}|#{TIMES}|#{DIVIDE}|#{MODULO}|#{PLUS_OR_MINUS})/
9
10
 
10
11
  def self.regex
11
12
  REGEX
@@ -20,6 +21,8 @@ module Keisan
20
21
  :*
21
22
  when DIVIDE
22
23
  :/
24
+ when MODULO
25
+ :%
23
26
  when PLUS_OR_MINUS
24
27
  string.count("-").even? ? :+ : :-
25
28
  end
@@ -0,0 +1,11 @@
1
+ module Keisan
2
+ module Tokens
3
+ class Dot < Token
4
+ REGEX = /(\.)/
5
+
6
+ def self.regex
7
+ REGEX
8
+ end
9
+ end
10
+ end
11
+ end
@@ -7,9 +7,11 @@ module Keisan
7
7
  GREATER_THAN = /(?:\>)/
8
8
  AND = /(?:\&\&)/
9
9
  OR = /(?:\|\|)/
10
+ EQUAL = /(?:\=\=)/
11
+ NOT_EQUAL = /(?:\!\=)/
10
12
  NOT = /(?:\!+)/
11
13
 
12
- REGEX = /(#{LESS_THAN_OR_EQUAL_TO}|#{GREATER_THAN_OR_EQUAL_TO}|#{LESS_THAN}|#{GREATER_THAN}|#{AND}|#{OR}|#{NOT})/
14
+ REGEX = /(#{LESS_THAN_OR_EQUAL_TO}|#{GREATER_THAN_OR_EQUAL_TO}|#{LESS_THAN}|#{GREATER_THAN}|#{AND}|#{OR}|#{EQUAL}|#{NOT_EQUAL}|#{NOT})/
13
15
 
14
16
  def self.regex
15
17
  REGEX
@@ -29,6 +31,10 @@ module Keisan
29
31
  :"&&"
30
32
  when OR
31
33
  :"||"
34
+ when EQUAL
35
+ :"=="
36
+ when NOT_EQUAL
37
+ :"!="
32
38
  when NOT
33
39
  string.count("!").even? ? :"!!" : :"!"
34
40
  end
@@ -18,6 +18,13 @@ module Keisan
18
18
  raise Keisan::Exceptions::UndefinedVariableError.new name
19
19
  end
20
20
 
21
+ def has?(name)
22
+ !!self[name]
23
+ rescue Keisan::Exceptions::UndefinedVariableError
24
+ false
25
+ end
26
+
27
+ # For checking if locally defined
21
28
  def has_name?(name)
22
29
  @hash.has_key?(name)
23
30
  end
@@ -1,3 +1,3 @@
1
1
  module Keisan
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: keisan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christopher Locke
@@ -125,18 +125,22 @@ files:
125
125
  - lib/keisan/ast/list.rb
126
126
  - lib/keisan/ast/literal.rb
127
127
  - lib/keisan/ast/logical_and.rb
128
+ - lib/keisan/ast/logical_equal.rb
128
129
  - lib/keisan/ast/logical_greater_than.rb
129
130
  - lib/keisan/ast/logical_greater_than_or_equal_to.rb
130
131
  - lib/keisan/ast/logical_less_than.rb
131
132
  - lib/keisan/ast/logical_less_than_or_equal_to.rb
133
+ - lib/keisan/ast/logical_not_equal.rb
132
134
  - lib/keisan/ast/logical_operator.rb
133
135
  - lib/keisan/ast/logical_or.rb
136
+ - lib/keisan/ast/modulo.rb
134
137
  - lib/keisan/ast/node.rb
135
138
  - lib/keisan/ast/null.rb
136
139
  - lib/keisan/ast/number.rb
137
140
  - lib/keisan/ast/operator.rb
138
141
  - lib/keisan/ast/parent.rb
139
142
  - lib/keisan/ast/plus.rb
143
+ - lib/keisan/ast/priorities.rb
140
144
  - lib/keisan/ast/string.rb
141
145
  - lib/keisan/ast/times.rb
142
146
  - lib/keisan/ast/unary_bitwise_not.rb
@@ -149,6 +153,7 @@ files:
149
153
  - lib/keisan/ast/variable.rb
150
154
  - lib/keisan/calculator.rb
151
155
  - lib/keisan/context.rb
156
+ - lib/keisan/evaluator.rb
152
157
  - lib/keisan/exceptions.rb
153
158
  - lib/keisan/function.rb
154
159
  - lib/keisan/functions/default_registry.rb
@@ -167,6 +172,9 @@ files:
167
172
  - lib/keisan/parsing/boolean.rb
168
173
  - lib/keisan/parsing/component.rb
169
174
  - lib/keisan/parsing/divide.rb
175
+ - lib/keisan/parsing/dot.rb
176
+ - lib/keisan/parsing/dot_operator.rb
177
+ - lib/keisan/parsing/dot_word.rb
170
178
  - lib/keisan/parsing/element.rb
171
179
  - lib/keisan/parsing/exponent.rb
172
180
  - lib/keisan/parsing/function.rb
@@ -174,15 +182,18 @@ files:
174
182
  - lib/keisan/parsing/indexing.rb
175
183
  - lib/keisan/parsing/list.rb
176
184
  - lib/keisan/parsing/logical_and.rb
185
+ - lib/keisan/parsing/logical_equal.rb
177
186
  - lib/keisan/parsing/logical_greater_than.rb
178
187
  - lib/keisan/parsing/logical_greater_than_or_equal_to.rb
179
188
  - lib/keisan/parsing/logical_less_than.rb
180
189
  - lib/keisan/parsing/logical_less_than_or_equal_to.rb
181
190
  - lib/keisan/parsing/logical_not.rb
191
+ - lib/keisan/parsing/logical_not_equal.rb
182
192
  - lib/keisan/parsing/logical_not_not.rb
183
193
  - lib/keisan/parsing/logical_operator.rb
184
194
  - lib/keisan/parsing/logical_or.rb
185
195
  - lib/keisan/parsing/minus.rb
196
+ - lib/keisan/parsing/modulo.rb
186
197
  - lib/keisan/parsing/null.rb
187
198
  - lib/keisan/parsing/number.rb
188
199
  - lib/keisan/parsing/operator.rb
@@ -201,6 +212,7 @@ files:
201
212
  - lib/keisan/tokens/bitwise_operator.rb
202
213
  - lib/keisan/tokens/boolean.rb
203
214
  - lib/keisan/tokens/comma.rb
215
+ - lib/keisan/tokens/dot.rb
204
216
  - lib/keisan/tokens/group.rb
205
217
  - lib/keisan/tokens/logical_operator.rb
206
218
  - lib/keisan/tokens/null.rb
@@ -231,7 +243,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
231
243
  version: '0'
232
244
  requirements: []
233
245
  rubyforge_project:
234
- rubygems_version: 2.6.10
246
+ rubygems_version: 2.6.11
235
247
  signing_key:
236
248
  specification_version: 4
237
249
  summary: An equation parser and evaluator