keisan 0.6.0 → 0.7.0

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