keisan 0.4.0 → 0.5.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -5
  3. data/README.md +112 -8
  4. data/bin/keisan +7 -0
  5. data/lib/keisan.rb +0 -1
  6. data/lib/keisan/ast.rb +24 -0
  7. data/lib/keisan/ast/assignment.rb +36 -10
  8. data/lib/keisan/ast/builder.rb +7 -1
  9. data/lib/keisan/ast/constant_literal.rb +6 -1
  10. data/lib/keisan/ast/exponent.rb +1 -1
  11. data/lib/keisan/ast/function.rb +7 -3
  12. data/lib/keisan/ast/list.rb +1 -1
  13. data/lib/keisan/ast/node.rb +1 -1
  14. data/lib/keisan/ast/parent.rb +1 -1
  15. data/lib/keisan/ast/plus.rb +11 -1
  16. data/lib/keisan/ast/string.rb +8 -0
  17. data/lib/keisan/ast/unary_operator.rb +1 -1
  18. data/lib/keisan/ast/variable.rb +8 -2
  19. data/lib/keisan/calculator.rb +16 -0
  20. data/lib/keisan/context.rb +39 -8
  21. data/lib/keisan/evaluator.rb +11 -1
  22. data/lib/keisan/function.rb +4 -0
  23. data/lib/keisan/functions/abs.rb +9 -0
  24. data/lib/keisan/functions/cbrt.rb +15 -0
  25. data/lib/keisan/functions/cmath_function.rb +11 -0
  26. data/lib/keisan/functions/cos.rb +15 -0
  27. data/lib/keisan/functions/cosh.rb +15 -0
  28. data/lib/keisan/functions/cot.rb +15 -0
  29. data/lib/keisan/functions/coth.rb +15 -0
  30. data/lib/keisan/functions/csc.rb +15 -0
  31. data/lib/keisan/functions/csch.rb +15 -0
  32. data/lib/keisan/functions/default_registry.rb +95 -1
  33. data/lib/keisan/functions/diff.rb +36 -14
  34. data/lib/keisan/functions/exp.rb +15 -0
  35. data/lib/keisan/functions/expression_function.rb +65 -15
  36. data/lib/keisan/functions/filter.rb +64 -0
  37. data/lib/keisan/functions/if.rb +15 -12
  38. data/lib/keisan/functions/imag.rb +9 -0
  39. data/lib/keisan/functions/log.rb +15 -0
  40. data/lib/keisan/functions/map.rb +57 -0
  41. data/lib/keisan/functions/math_function.rb +34 -0
  42. data/lib/keisan/functions/proc_function.rb +5 -3
  43. data/lib/keisan/functions/real.rb +9 -0
  44. data/lib/keisan/functions/reduce.rb +65 -0
  45. data/lib/keisan/functions/registry.rb +5 -1
  46. data/lib/keisan/functions/sec.rb +15 -0
  47. data/lib/keisan/functions/sech.rb +15 -0
  48. data/lib/keisan/functions/sin.rb +15 -0
  49. data/lib/keisan/functions/sinh.rb +15 -0
  50. data/lib/keisan/functions/sqrt.rb +15 -0
  51. data/lib/keisan/functions/tan.rb +15 -0
  52. data/lib/keisan/functions/tanh.rb +15 -0
  53. data/lib/keisan/repl.rb +105 -0
  54. data/lib/keisan/tokenizer.rb +5 -2
  55. data/lib/keisan/tokens/string.rb +1 -1
  56. data/lib/keisan/variables/registry.rb +14 -3
  57. data/lib/keisan/version.rb +1 -1
  58. data/screenshots/repl.png +0 -0
  59. metadata +30 -6
  60. data/lib/keisan/ast/functions/diff.rb +0 -57
  61. data/lib/keisan/ast/functions/if.rb +0 -47
  62. data/lib/keisan/function_definition_context.rb +0 -34
@@ -22,12 +22,15 @@ module Keisan
22
22
  attr_reader :expression, :tokens
23
23
 
24
24
  def initialize(expression)
25
- @expression = self.class.strip_whitespace(expression)
25
+ @expression = self.class.strip_whitespace_and_comments(expression)
26
26
  @scan = @expression.scan(TOKEN_REGEX)
27
27
  @tokens = tokenize!
28
28
  end
29
29
 
30
- def self.strip_whitespace(expression)
30
+ def self.strip_whitespace_and_comments(expression)
31
+ # Remove comments
32
+ expression = expression.split("#").first || ""
33
+
31
34
  # Do not allow whitespace between variables, numbers, and the like; they must be joined by operators
32
35
  raise Keisan::Exceptions::TokenizingError.new if expression.gsub(Tokens::String.regex, "").match /\w\s+\w/
33
36
 
@@ -1,7 +1,7 @@
1
1
  module Keisan
2
2
  module Tokens
3
3
  class String < Token
4
- REGEX = /(\"[^\"\']*\"|\'[^\"\']*\')/
4
+ REGEX = /(\"[^\"]*\"|\'[^\']*\')/
5
5
 
6
6
  def self.regex
7
7
  REGEX
@@ -1,8 +1,11 @@
1
1
  module Keisan
2
2
  module Variables
3
3
  class Registry
4
- def initialize(variables: {}, parent: nil, use_defaults: true, force: false)
4
+ attr_reader :shadowed
5
+
6
+ def initialize(variables: {}, shadowed: [], parent: nil, use_defaults: true, force: false)
5
7
  @hash = {}
8
+ @shadowed = Set.new(shadowed.map(&:to_s))
6
9
  @parent = parent
7
10
  @use_defaults = use_defaults
8
11
 
@@ -13,6 +16,7 @@ module Keisan
13
16
 
14
17
  def [](name)
15
18
  return @hash[name] if @hash.has_key?(name)
19
+ raise Keisan::Exceptions::UndefinedVariableError.new if @shadowed.include?(name)
16
20
 
17
21
  if @parent && (parent_value = @parent[name])
18
22
  return parent_value
@@ -22,18 +26,25 @@ module Keisan
22
26
  raise Keisan::Exceptions::UndefinedVariableError.new name
23
27
  end
24
28
 
29
+ def locals
30
+ @hash
31
+ end
32
+
25
33
  def has?(name)
26
- !!self[name]
34
+ !self[name].nil?
27
35
  rescue Keisan::Exceptions::UndefinedVariableError
28
36
  false
29
37
  end
30
38
 
31
39
  def register!(name, value, force: false)
40
+ name = name.to_s
41
+ name = name.name if name.is_a?(Keisan::AST::Variable)
42
+
32
43
  raise Keisan::Exceptions::UnmodifiableError.new("Cannot modify frozen variables registry") if frozen?
33
44
  if !force && @use_defaults && default_registry.has_name?(name)
34
45
  raise Keisan::Exceptions::UnmodifiableError.new("Cannot overwrite default variable")
35
46
  end
36
- self[name.to_s] = value
47
+ self[name.to_s] = value.to_node
37
48
  end
38
49
 
39
50
  protected
@@ -1,3 +1,3 @@
1
1
  module Keisan
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
Binary file
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.4.0
4
+ version: 0.5.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-04-04 00:00:00.000000000 Z
11
+ date: 2017-12-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -109,6 +109,7 @@ files:
109
109
  - README.md
110
110
  - Rakefile
111
111
  - bin/console
112
+ - bin/keisan
112
113
  - bin/setup
113
114
  - keisan.gemspec
114
115
  - lib/keisan.rb
@@ -124,8 +125,6 @@ files:
124
125
  - lib/keisan/ast/constant_literal.rb
125
126
  - lib/keisan/ast/exponent.rb
126
127
  - lib/keisan/ast/function.rb
127
- - lib/keisan/ast/functions/diff.rb
128
- - lib/keisan/ast/functions/if.rb
129
128
  - lib/keisan/ast/indexing.rb
130
129
  - lib/keisan/ast/list.rb
131
130
  - lib/keisan/ast/literal.rb
@@ -160,16 +159,39 @@ files:
160
159
  - lib/keisan/evaluator.rb
161
160
  - lib/keisan/exceptions.rb
162
161
  - lib/keisan/function.rb
163
- - lib/keisan/function_definition_context.rb
162
+ - lib/keisan/functions/abs.rb
163
+ - lib/keisan/functions/cbrt.rb
164
+ - lib/keisan/functions/cmath_function.rb
165
+ - lib/keisan/functions/cos.rb
166
+ - lib/keisan/functions/cosh.rb
167
+ - lib/keisan/functions/cot.rb
168
+ - lib/keisan/functions/coth.rb
169
+ - lib/keisan/functions/csc.rb
170
+ - lib/keisan/functions/csch.rb
164
171
  - lib/keisan/functions/default_registry.rb
165
172
  - lib/keisan/functions/diff.rb
173
+ - lib/keisan/functions/exp.rb
166
174
  - lib/keisan/functions/expression_function.rb
175
+ - lib/keisan/functions/filter.rb
167
176
  - lib/keisan/functions/if.rb
177
+ - lib/keisan/functions/imag.rb
178
+ - lib/keisan/functions/log.rb
179
+ - lib/keisan/functions/map.rb
180
+ - lib/keisan/functions/math_function.rb
168
181
  - lib/keisan/functions/proc_function.rb
169
182
  - lib/keisan/functions/rand.rb
183
+ - lib/keisan/functions/real.rb
184
+ - lib/keisan/functions/reduce.rb
170
185
  - lib/keisan/functions/registry.rb
171
186
  - lib/keisan/functions/replace.rb
172
187
  - lib/keisan/functions/sample.rb
188
+ - lib/keisan/functions/sec.rb
189
+ - lib/keisan/functions/sech.rb
190
+ - lib/keisan/functions/sin.rb
191
+ - lib/keisan/functions/sinh.rb
192
+ - lib/keisan/functions/sqrt.rb
193
+ - lib/keisan/functions/tan.rb
194
+ - lib/keisan/functions/tanh.rb
173
195
  - lib/keisan/parser.rb
174
196
  - lib/keisan/parsing/argument.rb
175
197
  - lib/keisan/parsing/arithmetic_operator.rb
@@ -217,6 +239,7 @@ files:
217
239
  - lib/keisan/parsing/unary_operator.rb
218
240
  - lib/keisan/parsing/unary_plus.rb
219
241
  - lib/keisan/parsing/variable.rb
242
+ - lib/keisan/repl.rb
220
243
  - lib/keisan/token.rb
221
244
  - lib/keisan/tokenizer.rb
222
245
  - lib/keisan/tokens/arithmetic_operator.rb
@@ -235,6 +258,7 @@ files:
235
258
  - lib/keisan/variables/default_registry.rb
236
259
  - lib/keisan/variables/registry.rb
237
260
  - lib/keisan/version.rb
261
+ - screenshots/repl.png
238
262
  homepage: https://github.com/project-eutopia/keisan
239
263
  licenses:
240
264
  - MIT
@@ -255,7 +279,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
255
279
  version: '0'
256
280
  requirements: []
257
281
  rubyforge_project:
258
- rubygems_version: 2.6.11
282
+ rubygems_version: 2.6.14
259
283
  signing_key:
260
284
  specification_version: 4
261
285
  summary: An equation parser and evaluator
@@ -1,57 +0,0 @@
1
- module Keisan
2
- module AST
3
- module Functions
4
- class Diff < AST::Function
5
- def initialize(arguments, name = "diff")
6
- super(arguments, name)
7
- end
8
-
9
- def value(context = nil)
10
- raise Keisan::Exceptions::InvalidFunctionError.new("Derivative not defined")
11
- end
12
-
13
- def unbound_functions(context = nil)
14
- context ||= Keisan::Context.new
15
-
16
- children.inject(Set.new) do |res, child|
17
- res | child.unbound_functions(context)
18
- end
19
- end
20
-
21
- def simplify(context = nil)
22
- unless children.size > 0
23
- raise Keisan::Exceptions::InvalidFunctionError.new("Diff requires at least one argument")
24
- end
25
-
26
- vars = children[1..-1]
27
-
28
- unless vars.all? {|var| var.is_a?(AST::Variable)}
29
- raise Keisan::Exceptions::InvalidFunctionError.new("Diff must differentiate with respect to variables")
30
- end
31
-
32
- result = children.first.simplify(context)
33
-
34
- while vars.size > 0
35
- begin
36
- var = vars.first
37
- if result.unbound_variables(context).include?(var.name)
38
- result = result.differentiate(var, context)
39
- else
40
- return AST::Number.new(0)
41
- end
42
- rescue Keisan::Exceptions::NonDifferentiableError => e
43
- return AST::Functions::Diff.new(
44
- [result] + vars,
45
- "diff"
46
- )
47
- end
48
-
49
- vars.shift
50
- end
51
-
52
- result
53
- end
54
- end
55
- end
56
- end
57
- end
@@ -1,47 +0,0 @@
1
- module Keisan
2
- module AST
3
- module Functions
4
- class If < AST::Function
5
- def value(context = nil)
6
- unless (2..3).cover? children.size
7
- raise Keisan::Exceptions::InvalidFunctionError.new("Require 2 or 3 arguments to if")
8
- end
9
-
10
- context ||= Context.new
11
-
12
- bool = children[0].value(context)
13
-
14
- if bool
15
- children[1].value(context)
16
- else
17
- children.size == 3 ? children[2].value(context) : nil
18
- end
19
- end
20
-
21
- def simplify(context = nil)
22
- context ||= Context.new
23
- @children = children.map {|child| child.simplify(context)}
24
-
25
- if children[0].is_a?(AST::Boolean)
26
- if children[0].value
27
- children[1]
28
- else
29
- # If no third argument, then children[2] gives nil, and to_node makes this AST::Null
30
- children[2].to_node
31
- end
32
- else
33
- self
34
- end
35
- end
36
-
37
- def unbound_functions(context = nil)
38
- context ||= Context.new
39
-
40
- children.inject(Set.new) do |res, child|
41
- res | child.unbound_functions(context)
42
- end
43
- end
44
- end
45
- end
46
- end
47
- end
@@ -1,34 +0,0 @@
1
- module Keisan
2
- class FunctionDefinitionContext < Context
3
- def initialize(parent:, arguments:)
4
- super(parent: parent)
5
- @arguments = Set.new(arguments)
6
- @arguments_context = Context.new
7
- set_transient!
8
- end
9
-
10
- def variable(name)
11
- if @arguments.member?(name)
12
- @arguments_context.variable(name)
13
- else
14
- super
15
- end
16
- end
17
-
18
- def has_variable?(name)
19
- if @arguments.member?(name)
20
- @arguments_context.has_variable?(name)
21
- else
22
- super
23
- end
24
- end
25
-
26
- def register_variable!(name, value)
27
- if @arguments.member?(name)
28
- @arguments_context.register_variable!(name, value)
29
- else
30
- super
31
- end
32
- end
33
- end
34
- end