keisan 0.4.0 → 0.5.0

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