keisan 0.6.0 → 0.7.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +90 -132
  3. data/bin/keisan +13 -2
  4. data/lib/keisan.rb +5 -0
  5. data/lib/keisan/ast.rb +11 -0
  6. data/lib/keisan/ast/assignment.rb +20 -55
  7. data/lib/keisan/ast/boolean.rb +16 -12
  8. data/lib/keisan/ast/cell.rb +17 -0
  9. data/lib/keisan/ast/cell_assignment.rb +70 -0
  10. data/lib/keisan/ast/function_assignment.rb +52 -0
  11. data/lib/keisan/ast/hash.rb +82 -0
  12. data/lib/keisan/ast/indexing.rb +26 -15
  13. data/lib/keisan/ast/line_builder.rb +22 -4
  14. data/lib/keisan/ast/list.rb +14 -7
  15. data/lib/keisan/ast/node.rb +13 -0
  16. data/lib/keisan/ast/null.rb +14 -0
  17. data/lib/keisan/ast/parent.rb +8 -3
  18. data/lib/keisan/ast/string.rb +10 -0
  19. data/lib/keisan/ast/variable.rb +4 -0
  20. data/lib/keisan/ast/variable_assignment.rb +62 -0
  21. data/lib/keisan/context.rb +16 -2
  22. data/lib/keisan/functions/default_registry.rb +4 -0
  23. data/lib/keisan/functions/enumerable_function.rb +56 -0
  24. data/lib/keisan/functions/filter.rb +34 -32
  25. data/lib/keisan/functions/map.rb +25 -31
  26. data/lib/keisan/functions/puts.rb +23 -0
  27. data/lib/keisan/functions/reduce.rb +29 -29
  28. data/lib/keisan/functions/registry.rb +4 -4
  29. data/lib/keisan/functions/sample.rb +5 -3
  30. data/lib/keisan/functions/to_h.rb +34 -0
  31. data/lib/keisan/interpreter.rb +42 -0
  32. data/lib/keisan/parser.rb +59 -50
  33. data/lib/keisan/parsing/compound_assignment.rb +15 -0
  34. data/lib/keisan/parsing/hash.rb +36 -0
  35. data/lib/keisan/repl.rb +1 -1
  36. data/lib/keisan/token.rb +1 -0
  37. data/lib/keisan/tokenizer.rb +23 -19
  38. data/lib/keisan/tokens/assignment.rb +21 -1
  39. data/lib/keisan/tokens/colon.rb +11 -0
  40. data/lib/keisan/tokens/unknown.rb +11 -0
  41. data/lib/keisan/variables/registry.rb +11 -6
  42. data/lib/keisan/version.rb +1 -1
  43. metadata +14 -2
@@ -0,0 +1,36 @@
1
+ module Keisan
2
+ module Parsing
3
+ class Hash < Element
4
+ attr_reader :key_value_pairs
5
+
6
+ def initialize(key_value_pairs)
7
+ @key_value_pairs = Array.wrap(key_value_pairs).map {|key_value_pair|
8
+ validate_and_extract_key_value_pair(key_value_pair)
9
+ }
10
+ end
11
+
12
+ private
13
+
14
+ def validate_and_extract_key_value_pair(key_value_pair)
15
+ key, value = key_value_pair.split {|token| token.is_a?(Tokens::Colon)}
16
+ raise Exceptions::ParseError.new("Invalid hash") unless key.size == 1 && value.size >= 1
17
+
18
+ key = key.first
19
+ if allowed_key?(key)
20
+ [Parsing::String.new(key.value), Parsing::RoundGroup.new(value)]
21
+ else
22
+ raise Exceptions::ParseError.new("Invalid hash (keys must be constants)")
23
+ end
24
+ end
25
+
26
+ def allowed_key?(key)
27
+ case key
28
+ when Tokens::String, Tokens::Boolean, Tokens::Null, Tokens::Number
29
+ true
30
+ else
31
+ false
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,4 +1,4 @@
1
- require "pry"
1
+ require "coderay"
2
2
  require "readline"
3
3
 
4
4
  module Keisan
@@ -1,6 +1,7 @@
1
1
  module Keisan
2
2
  class Token
3
3
  attr_reader :string
4
+
4
5
  def initialize(string)
5
6
  raise Exceptions::InvalidToken.new(string) unless string.match(regex)
6
7
  @string = string
@@ -7,13 +7,15 @@ module Keisan
7
7
  Tokens::Boolean,
8
8
  Tokens::Word,
9
9
  Tokens::Number,
10
- Tokens::ArithmeticOperator,
10
+ Tokens::Assignment,
11
11
  Tokens::LogicalOperator,
12
+ Tokens::ArithmeticOperator,
12
13
  Tokens::BitwiseOperator,
13
- Tokens::Assignment,
14
14
  Tokens::Comma,
15
+ Tokens::Colon,
15
16
  Tokens::Dot,
16
- Tokens::LineSeparator
17
+ Tokens::LineSeparator,
18
+ Tokens::Unknown
17
19
  ]
18
20
 
19
21
  TOKEN_REGEX = Regexp::new(
@@ -23,34 +25,36 @@ module Keisan
23
25
  attr_reader :expression, :tokens
24
26
 
25
27
  def initialize(expression)
26
- @expression = self.class.strip_whitespace_and_comments(expression)
28
+ @expression = self.class.normalize_expression(expression)
27
29
  @scan = @expression.scan(TOKEN_REGEX)
28
30
  @tokens = tokenize!
29
31
  end
30
32
 
31
- def self.strip_whitespace_and_comments(expression)
32
- # Remove comments
33
- expression = expression.split("#").first || ""
34
- expression = expression.gsub(/\n/, ";")
35
-
36
- # Only strip whitespace outside of strings, e.g.
37
- # "1 + 2 + 'hello world'" => "1+2+'hello world'"
38
- expression.split(Tokens::String.regex).map.with_index {|s,i| i.even? ? s.gsub(/\s+/, " ") : s}.join
33
+ def self.normalize_expression(expression)
34
+ expression = normalize_line_delimiters(expression)
35
+ expression = remove_comments(expression)
39
36
  end
40
37
 
41
38
  private
42
39
 
43
- def tokenize!
44
- tokenizing_check = ""
40
+ def self.normalize_line_delimiters(expression)
41
+ expression.gsub(/\n/, ";")
42
+ end
43
+
44
+ def self.remove_comments(expression)
45
+ expression.gsub(/#[^;]*/, "")
46
+ end
45
47
 
46
- tokens = @scan.map do |scan_result|
48
+ def tokenize!
49
+ @scan.map do |scan_result|
47
50
  i = scan_result.find_index {|token| !token.nil?}
48
51
  token_string = scan_result[i]
49
- tokenizing_check << token_string
50
- token_class = TOKEN_CLASSES[i].new(token_string)
52
+ token = TOKEN_CLASSES[i].new(token_string)
53
+ if token.is_a?(Tokens::Unknown)
54
+ raise Keisan::Exceptions::TokenizingError.new("Unexpected token: \"#{token.string}\"")
55
+ end
56
+ token
51
57
  end
52
-
53
- tokens
54
58
  end
55
59
  end
56
60
  end
@@ -1,7 +1,23 @@
1
1
  module Keisan
2
2
  module Tokens
3
3
  class Assignment < Operator
4
- REGEX = /(\=)/
4
+ REGEX = /(
5
+ (?: # possible compound operators in front of equals
6
+ \|\| |
7
+ \&\& |
8
+ \*\* |
9
+ \+ |
10
+ \- |
11
+ \* |
12
+ \/ |
13
+ \% |
14
+ \& |
15
+ \| |
16
+ \^
17
+ )?
18
+ \=
19
+ (?!\=) # negative lookahead to prevent matching ==
20
+ )/x
5
21
 
6
22
  def self.regex
7
23
  REGEX
@@ -10,6 +26,10 @@ module Keisan
10
26
  def operator_type
11
27
  :"="
12
28
  end
29
+
30
+ def compound_operator
31
+ string[0] == "=" ? nil : string[0...-1].to_sym
32
+ end
13
33
  end
14
34
  end
15
35
  end
@@ -0,0 +1,11 @@
1
+ module Keisan
2
+ module Tokens
3
+ class Colon < Token
4
+ REGEX = /(\:)/
5
+
6
+ def self.regex
7
+ REGEX
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Keisan
2
+ module Tokens
3
+ class Unknown < Token
4
+ REGEX = /([^[[:space:]]]+?)/
5
+
6
+ def self.regex
7
+ REGEX
8
+ end
9
+ end
10
+ end
11
+ end
@@ -26,6 +26,11 @@ module Keisan
26
26
  raise Exceptions::UndefinedVariableError.new name
27
27
  end
28
28
 
29
+ def freeze
30
+ @hash.values.each(&:freeze)
31
+ super
32
+ end
33
+
29
34
  def locals
30
35
  @hash
31
36
  end
@@ -36,15 +41,15 @@ module Keisan
36
41
  false
37
42
  end
38
43
 
44
+ def modifiable?(name)
45
+ !frozen? && has?(name)
46
+ end
47
+
39
48
  def register!(name, value, force: false)
49
+ raise Exceptions::UnmodifiableError.new("Cannot modify frozen variables registry") if frozen?
40
50
  name = name.to_s
41
- name = name.name if name.is_a?(AST::Variable)
42
51
 
43
- raise Exceptions::UnmodifiableError.new("Cannot modify frozen variables registry") if frozen?
44
- if !force && @use_defaults && default_registry.has_name?(name)
45
- raise Exceptions::UnmodifiableError.new("Cannot overwrite default variable")
46
- end
47
- self[name.to_s] = AST::Cell.new(value.to_node)
52
+ self[name.to_s] = value.to_node.to_cell
48
53
  end
49
54
 
50
55
  protected
@@ -1,3 +1,3 @@
1
1
  module Keisan
2
- VERSION = "0.6.0"
2
+ VERSION = "0.7.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: keisan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christopher Locke
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-27 00:00:00.000000000 Z
11
+ date: 2018-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -138,9 +138,12 @@ files:
138
138
  - lib/keisan/ast/boolean.rb
139
139
  - lib/keisan/ast/builder.rb
140
140
  - lib/keisan/ast/cell.rb
141
+ - lib/keisan/ast/cell_assignment.rb
141
142
  - lib/keisan/ast/constant_literal.rb
142
143
  - lib/keisan/ast/exponent.rb
143
144
  - lib/keisan/ast/function.rb
145
+ - lib/keisan/ast/function_assignment.rb
146
+ - lib/keisan/ast/hash.rb
144
147
  - lib/keisan/ast/indexing.rb
145
148
  - lib/keisan/ast/line_builder.rb
146
149
  - lib/keisan/ast/list.rb
@@ -172,6 +175,7 @@ files:
172
175
  - lib/keisan/ast/unary_operator.rb
173
176
  - lib/keisan/ast/unary_plus.rb
174
177
  - lib/keisan/ast/variable.rb
178
+ - lib/keisan/ast/variable_assignment.rb
175
179
  - lib/keisan/calculator.rb
176
180
  - lib/keisan/context.rb
177
181
  - lib/keisan/evaluator.rb
@@ -188,6 +192,7 @@ files:
188
192
  - lib/keisan/functions/csch.rb
189
193
  - lib/keisan/functions/default_registry.rb
190
194
  - lib/keisan/functions/diff.rb
195
+ - lib/keisan/functions/enumerable_function.rb
191
196
  - lib/keisan/functions/erf.rb
192
197
  - lib/keisan/functions/exp.rb
193
198
  - lib/keisan/functions/expression_function.rb
@@ -199,6 +204,7 @@ files:
199
204
  - lib/keisan/functions/map.rb
200
205
  - lib/keisan/functions/math_function.rb
201
206
  - lib/keisan/functions/proc_function.rb
207
+ - lib/keisan/functions/puts.rb
202
208
  - lib/keisan/functions/rand.rb
203
209
  - lib/keisan/functions/range.rb
204
210
  - lib/keisan/functions/real.rb
@@ -213,7 +219,9 @@ files:
213
219
  - lib/keisan/functions/sqrt.rb
214
220
  - lib/keisan/functions/tan.rb
215
221
  - lib/keisan/functions/tanh.rb
222
+ - lib/keisan/functions/to_h.rb
216
223
  - lib/keisan/functions/while.rb
224
+ - lib/keisan/interpreter.rb
217
225
  - lib/keisan/parser.rb
218
226
  - lib/keisan/parsing/argument.rb
219
227
  - lib/keisan/parsing/arithmetic_operator.rb
@@ -226,6 +234,7 @@ files:
226
234
  - lib/keisan/parsing/bitwise_xor.rb
227
235
  - lib/keisan/parsing/boolean.rb
228
236
  - lib/keisan/parsing/component.rb
237
+ - lib/keisan/parsing/compound_assignment.rb
229
238
  - lib/keisan/parsing/curly_group.rb
230
239
  - lib/keisan/parsing/divide.rb
231
240
  - lib/keisan/parsing/dot.rb
@@ -235,6 +244,7 @@ files:
235
244
  - lib/keisan/parsing/exponent.rb
236
245
  - lib/keisan/parsing/function.rb
237
246
  - lib/keisan/parsing/group.rb
247
+ - lib/keisan/parsing/hash.rb
238
248
  - lib/keisan/parsing/indexing.rb
239
249
  - lib/keisan/parsing/line_separator.rb
240
250
  - lib/keisan/parsing/list.rb
@@ -270,6 +280,7 @@ files:
270
280
  - lib/keisan/tokens/assignment.rb
271
281
  - lib/keisan/tokens/bitwise_operator.rb
272
282
  - lib/keisan/tokens/boolean.rb
283
+ - lib/keisan/tokens/colon.rb
273
284
  - lib/keisan/tokens/comma.rb
274
285
  - lib/keisan/tokens/dot.rb
275
286
  - lib/keisan/tokens/group.rb
@@ -279,6 +290,7 @@ files:
279
290
  - lib/keisan/tokens/number.rb
280
291
  - lib/keisan/tokens/operator.rb
281
292
  - lib/keisan/tokens/string.rb
293
+ - lib/keisan/tokens/unknown.rb
282
294
  - lib/keisan/tokens/word.rb
283
295
  - lib/keisan/variables/default_registry.rb
284
296
  - lib/keisan/variables/registry.rb