keisan 0.2.1 → 0.3.0

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