keisan 0.3.0 → 0.4.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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +57 -9
  3. data/lib/keisan.rb +21 -12
  4. data/lib/keisan/ast.rb +50 -0
  5. data/lib/keisan/ast/arithmetic_operator.rb +0 -3
  6. data/lib/keisan/ast/assignment.rb +72 -0
  7. data/lib/keisan/ast/bitwise_and.rb +4 -4
  8. data/lib/keisan/ast/bitwise_operator.rb +0 -3
  9. data/lib/keisan/ast/bitwise_or.rb +4 -4
  10. data/lib/keisan/ast/bitwise_xor.rb +4 -4
  11. data/lib/keisan/ast/boolean.rb +25 -1
  12. data/lib/keisan/ast/builder.rb +98 -63
  13. data/lib/keisan/ast/constant_literal.rb +13 -0
  14. data/lib/keisan/ast/exponent.rb +62 -8
  15. data/lib/keisan/ast/function.rb +37 -26
  16. data/lib/keisan/ast/functions/diff.rb +57 -0
  17. data/lib/keisan/ast/functions/if.rb +47 -0
  18. data/lib/keisan/ast/indexing.rb +44 -4
  19. data/lib/keisan/ast/list.rb +4 -0
  20. data/lib/keisan/ast/literal.rb +8 -0
  21. data/lib/keisan/ast/logical_and.rb +9 -4
  22. data/lib/keisan/ast/logical_equal.rb +4 -4
  23. data/lib/keisan/ast/logical_greater_than.rb +4 -4
  24. data/lib/keisan/ast/logical_greater_than_or_equal_to.rb +4 -4
  25. data/lib/keisan/ast/logical_less_than.rb +4 -4
  26. data/lib/keisan/ast/logical_less_than_or_equal_to.rb +4 -4
  27. data/lib/keisan/ast/logical_not_equal.rb +4 -4
  28. data/lib/keisan/ast/logical_operator.rb +0 -3
  29. data/lib/keisan/ast/logical_or.rb +10 -5
  30. data/lib/keisan/ast/modulo.rb +4 -4
  31. data/lib/keisan/ast/node.rb +132 -20
  32. data/lib/keisan/ast/null.rb +1 -1
  33. data/lib/keisan/ast/number.rb +172 -1
  34. data/lib/keisan/ast/operator.rb +66 -8
  35. data/lib/keisan/ast/parent.rb +50 -0
  36. data/lib/keisan/ast/plus.rb +38 -4
  37. data/lib/keisan/ast/string.rb +10 -1
  38. data/lib/keisan/ast/times.rb +47 -4
  39. data/lib/keisan/ast/unary_bitwise_not.rb +9 -1
  40. data/lib/keisan/ast/unary_identity.rb +18 -1
  41. data/lib/keisan/ast/unary_inverse.rb +35 -1
  42. data/lib/keisan/ast/unary_logical_not.rb +5 -1
  43. data/lib/keisan/ast/unary_minus.rb +31 -1
  44. data/lib/keisan/ast/unary_operator.rb +29 -1
  45. data/lib/keisan/ast/unary_plus.rb +14 -1
  46. data/lib/keisan/ast/variable.rb +44 -0
  47. data/lib/keisan/calculator.rb +2 -2
  48. data/lib/keisan/context.rb +32 -16
  49. data/lib/keisan/evaluator.rb +10 -65
  50. data/lib/keisan/exceptions.rb +2 -0
  51. data/lib/keisan/function.rb +11 -5
  52. data/lib/keisan/function_definition_context.rb +34 -0
  53. data/lib/keisan/functions/default_registry.rb +13 -5
  54. data/lib/keisan/functions/diff.rb +82 -0
  55. data/lib/keisan/functions/expression_function.rb +63 -0
  56. data/lib/keisan/functions/if.rb +62 -0
  57. data/lib/keisan/functions/proc_function.rb +52 -0
  58. data/lib/keisan/functions/rand.rb +1 -1
  59. data/lib/keisan/functions/registry.rb +20 -10
  60. data/lib/keisan/functions/replace.rb +49 -0
  61. data/lib/keisan/functions/sample.rb +1 -1
  62. data/lib/keisan/parser.rb +13 -1
  63. data/lib/keisan/parsing/assignment.rb +9 -0
  64. data/lib/keisan/parsing/operator.rb +8 -0
  65. data/lib/keisan/parsing/unary_operator.rb +1 -1
  66. data/lib/keisan/tokenizer.rb +1 -0
  67. data/lib/keisan/tokens/assignment.rb +15 -0
  68. data/lib/keisan/variables/default_registry.rb +4 -4
  69. data/lib/keisan/variables/registry.rb +17 -8
  70. data/lib/keisan/version.rb +1 -1
  71. metadata +15 -3
  72. data/lib/keisan/ast/priorities.rb +0 -27
@@ -0,0 +1,47 @@
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,15 +1,55 @@
1
1
  module Keisan
2
2
  module AST
3
3
  class Indexing < UnaryOperator
4
- attr_reader :arguments
4
+ attr_reader :indexes
5
5
 
6
- def initialize(child, arguments = [])
6
+ def initialize(child, indexes = [])
7
7
  @children = [child]
8
- @arguments = arguments
8
+ @indexes = indexes
9
9
  end
10
10
 
11
11
  def value(context = nil)
12
- return children.first.value(context).send(:[], *arguments.map {|arg| arg.value(context)})
12
+ return child.value(context).send(:[], *indexes.map {|index| index.value(context)})
13
+ end
14
+
15
+ def to_s
16
+ "(#{child.to_s})[#{indexes.map(&:to_s).join(',')}]"
17
+ end
18
+
19
+ def evaluate(context = nil)
20
+ context ||= Keisan::Context.new
21
+ @children = children.map {|child| child.evaluate(context)}
22
+ @indexes = indexes.map {|index| index.evaluate(context)}
23
+
24
+ case child
25
+ when AST::List
26
+ if @indexes.size == 1 && @indexes.first.is_a?(AST::Number)
27
+ return child.children[@indexes.first.value(context)].evaluate(context)
28
+ end
29
+ end
30
+
31
+ self
32
+ end
33
+
34
+ def simplify(context = nil)
35
+ context ||= Context.new
36
+
37
+ @indexes = indexes.map {|index| index.simplify(context)}
38
+ @children = [child.simplify(context)]
39
+
40
+ case child
41
+ when AST::List
42
+ if @indexes.size == 1 && @indexes.first.is_a?(AST::Number)
43
+ return child.children[@indexes.first.value(context)].simplify(context)
44
+ end
45
+ end
46
+
47
+ self
48
+ end
49
+
50
+ def replace(variable, replacement)
51
+ super
52
+ @indexes = indexes.map {|index| index.replace(variable, replacement)}
13
53
  end
14
54
  end
15
55
  end
@@ -5,6 +5,10 @@ module Keisan
5
5
  context = Keisan::Context.new if context.nil?
6
6
  children.map {|child| child.value(context)}
7
7
  end
8
+
9
+ def to_s
10
+ "[#{children.map(&:to_s).join(',')}]"
11
+ end
8
12
  end
9
13
  end
10
14
  end
@@ -1,6 +1,14 @@
1
1
  module Keisan
2
2
  module AST
3
3
  class Literal < Node
4
+ def ==(other)
5
+ case other
6
+ when Literal
7
+ value == other.value
8
+ else
9
+ false
10
+ end
11
+ end
4
12
  end
5
13
  end
6
14
  end
@@ -1,17 +1,22 @@
1
1
  module Keisan
2
2
  module AST
3
3
  class LogicalAnd < LogicalOperator
4
- def arity
5
- 2..Float::INFINITY
4
+ def self.symbol
5
+ :"&&"
6
6
  end
7
7
 
8
- def self.symbol
9
- :"&"
8
+ def evaluate(context = nil)
9
+ children[0].evaluate(context).and(children[1].evaluate(context))
10
10
  end
11
11
 
12
12
  def blank_value
13
13
  true
14
14
  end
15
+
16
+ def value(context = nil)
17
+ context ||= Keisan::Context.new
18
+ children[0].value(context) && children[1].value(context)
19
+ end
15
20
  end
16
21
  end
17
22
  end
@@ -1,14 +1,14 @@
1
1
  module Keisan
2
2
  module AST
3
3
  class LogicalEqual < LogicalOperator
4
- def arity
5
- 2..2
6
- end
7
-
8
4
  def self.symbol
9
5
  :"=="
10
6
  end
11
7
 
8
+ def evaluate(context = nil)
9
+ children[0].evaluate(context).equal(children[1].evaluate(context))
10
+ end
11
+
12
12
  def value(context=nil)
13
13
  context ||= Context.new
14
14
  children[0].value(context) == children[1].value(context)
@@ -1,14 +1,14 @@
1
1
  module Keisan
2
2
  module AST
3
3
  class LogicalGreaterThan < LogicalOperator
4
- def arity
5
- 2..2
6
- end
7
-
8
4
  def self.symbol
9
5
  :">"
10
6
  end
11
7
 
8
+ def evaluate(context = nil)
9
+ children[0].evaluate(context) > children[1].evaluate(context)
10
+ end
11
+
12
12
  def value(context = nil)
13
13
  children.first.value(context) > children.last.value(context)
14
14
  end
@@ -1,14 +1,14 @@
1
1
  module Keisan
2
2
  module AST
3
3
  class LogicalGreaterThanOrEqualTo < LogicalOperator
4
- def arity
5
- 2..2
6
- end
7
-
8
4
  def self.symbol
9
5
  :">="
10
6
  end
11
7
 
8
+ def evaluate(context = nil)
9
+ children[0].evaluate(context) >= children[1].evaluate(context)
10
+ end
11
+
12
12
  def value(context = nil)
13
13
  children.first.value(context) >= children.last.value(context)
14
14
  end
@@ -1,14 +1,14 @@
1
1
  module Keisan
2
2
  module AST
3
3
  class LogicalLessThan < LogicalOperator
4
- def arity
5
- 2..2
6
- end
7
-
8
4
  def self.symbol
9
5
  :"<"
10
6
  end
11
7
 
8
+ def evaluate(context = nil)
9
+ children[0].evaluate(context) < children[1].evaluate(context)
10
+ end
11
+
12
12
  def value(context = nil)
13
13
  children.first.value(context) < children.last.value(context)
14
14
  end
@@ -1,14 +1,14 @@
1
1
  module Keisan
2
2
  module AST
3
3
  class LogicalLessThanOrEqualTo < LogicalOperator
4
- def arity
5
- 2..2
6
- end
7
-
8
4
  def self.symbol
9
5
  :"<="
10
6
  end
11
7
 
8
+ def evaluate(context = nil)
9
+ children[0].evaluate(context) <= children[1].evaluate(context)
10
+ end
11
+
12
12
  def value(context = nil)
13
13
  children.first.value(context) <= children.last.value(context)
14
14
  end
@@ -1,14 +1,14 @@
1
1
  module Keisan
2
2
  module AST
3
3
  class LogicalNotEqual < LogicalOperator
4
- def arity
5
- 2..2
6
- end
7
-
8
4
  def self.symbol
9
5
  :"!="
10
6
  end
11
7
 
8
+ def evaluate(context = nil)
9
+ children[0].evaluate(context).not_equal(children[1].evaluate(context))
10
+ end
11
+
12
12
  def value(context=nil)
13
13
  context ||= Context.new
14
14
  children[0].value(context) != children[1].value(context)
@@ -1,9 +1,6 @@
1
1
  module Keisan
2
2
  module AST
3
3
  class LogicalOperator < Operator
4
- def associativity
5
- :left
6
- end
7
4
  end
8
5
  end
9
6
  end
@@ -1,17 +1,22 @@
1
1
  module Keisan
2
2
  module AST
3
3
  class LogicalOr < LogicalOperator
4
- def arity
5
- 2..Float::INFINITY
6
- end
7
-
8
4
  def self.symbol
9
- :"|"
5
+ :"||"
10
6
  end
11
7
 
12
8
  def blank_value
13
9
  false
14
10
  end
11
+
12
+ def evaluate(context = nil)
13
+ children[0].evaluate(context).or(children[1].evaluate(context))
14
+ end
15
+
16
+ def value(context = nil)
17
+ context ||= Keisan::Context.new
18
+ children[0].value(context) || children[1].value(context)
19
+ end
15
20
  end
16
21
  end
17
22
  end
@@ -1,14 +1,14 @@
1
1
  module Keisan
2
2
  module AST
3
3
  class Modulo < ArithmeticOperator
4
- def arity
5
- 2..Float::INFINITY
6
- end
7
-
8
4
  def self.symbol
9
5
  :%
10
6
  end
11
7
 
8
+ def evaluate(context = nil)
9
+ children[1..-1].inject(children.first.evaluate(context)) {|total, child| total % child.evaluate(context)}
10
+ end
11
+
12
12
  def value(context = nil)
13
13
  children_values = children.map {|child| child.value(context)}
14
14
  children_values[1..-1].inject(children_values[0], &:%)
@@ -6,29 +6,141 @@ module Keisan
6
6
  end
7
7
 
8
8
  def unbound_variables(context = nil)
9
- context ||= Keisan::Context.new
10
-
11
- case self
12
- when Parent
13
- children.inject(Set.new) do |vars, child|
14
- vars | child.unbound_variables(context)
15
- end
16
- else
17
- Set.new
18
- end
9
+ Set.new
19
10
  end
20
11
 
21
12
  def unbound_functions(context = nil)
22
- context ||= Keisan::Context.new
23
-
24
- case self
25
- when Parent
26
- children.inject(Set.new) do |fns, child|
27
- fns | child.unbound_functions(context)
28
- end
29
- else
30
- Set.new
31
- end
13
+ Set.new
14
+ end
15
+
16
+ def well_defined?(context = nil)
17
+ unbound_variables(context).empty? && unbound_functions(context).empty?
18
+ end
19
+
20
+ def deep_dup
21
+ dup
22
+ end
23
+
24
+ def simplified(context = nil)
25
+ deep_dup.simplify(context)
26
+ end
27
+
28
+ def simplify(context = nil)
29
+ self
30
+ end
31
+
32
+ def evaluate(context = nil)
33
+ self
34
+ end
35
+
36
+ def differentiate(variable, context = nil)
37
+ raise Keisan::Exceptions::NonDifferentiableError.new
38
+ end
39
+
40
+ def replace(variable, replacement)
41
+ self
42
+ end
43
+
44
+ def coerce(other)
45
+ [other.to_node, self]
46
+ end
47
+
48
+ def to_node
49
+ self
50
+ end
51
+
52
+ def +(other)
53
+ AST::Plus.new(
54
+ [self, other.to_node]
55
+ )
56
+ end
57
+
58
+ def -(other)
59
+ AST::Plus.new(
60
+ [self, AST::UnaryMinus.new(other.to_node)]
61
+ )
62
+ end
63
+
64
+ def *(other)
65
+ AST::Times.new(
66
+ [self, other.to_node]
67
+ )
68
+ end
69
+
70
+ def /(other)
71
+ AST::Times.new(
72
+ [self, AST::UnaryInverse.new(other.to_node)]
73
+ )
74
+ end
75
+
76
+ def %(other)
77
+ AST::Modulo.new(
78
+ [self, other.to_node]
79
+ )
80
+ end
81
+
82
+ def !
83
+ AST::LogicalNot.new(self)
84
+ end
85
+
86
+ def ~
87
+ AST::UnaryBitwiseNot.new(self)
88
+ end
89
+
90
+ def +@
91
+ self
92
+ end
93
+
94
+ def -@
95
+ AST::UnaryMinus.new(self)
96
+ end
97
+
98
+ def **(other)
99
+ AST::Exponent.new([self, other.to_node])
100
+ end
101
+
102
+ def &(other)
103
+ AST::BitwiseAnd.new([self, other.to_node])
104
+ end
105
+
106
+ def ^(other)
107
+ AST::BitwiseXor.new([self, other.to_node])
108
+ end
109
+
110
+ def |(other)
111
+ AST::BitwiseOr.new([self, other.to_node])
112
+ end
113
+
114
+ def >(other)
115
+ AST::LogicalGreaterThan.new([self, other.to_node])
116
+ end
117
+
118
+ def >=(other)
119
+ AST::LogicalGreaterThanOrEqualTo.new([self, other.to_node])
120
+ end
121
+
122
+ def <(other)
123
+ AST::LogicalLessThan.new([self, other.to_node])
124
+ end
125
+
126
+ def <=(other)
127
+ AST::LogicalLessThanOrEqualTo.new([self, other.to_node])
128
+ end
129
+
130
+ def equal(other)
131
+ AST::LogicalEqual.new([self, other.to_node])
132
+ end
133
+
134
+ def not_equal(other)
135
+ AST::LogicalNotEqual.new([self, other.to_node])
136
+ end
137
+
138
+ def and(other)
139
+ AST::LogicalAnd.new([self, other.to_node])
140
+ end
141
+
142
+ def or(other)
143
+ AST::LogicalOr.new([self, other.to_node])
32
144
  end
33
145
  end
34
146
  end