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.
- checksums.yaml +4 -4
- data/README.md +57 -9
- data/lib/keisan.rb +21 -12
- data/lib/keisan/ast.rb +50 -0
- data/lib/keisan/ast/arithmetic_operator.rb +0 -3
- data/lib/keisan/ast/assignment.rb +72 -0
- data/lib/keisan/ast/bitwise_and.rb +4 -4
- data/lib/keisan/ast/bitwise_operator.rb +0 -3
- data/lib/keisan/ast/bitwise_or.rb +4 -4
- data/lib/keisan/ast/bitwise_xor.rb +4 -4
- data/lib/keisan/ast/boolean.rb +25 -1
- data/lib/keisan/ast/builder.rb +98 -63
- data/lib/keisan/ast/constant_literal.rb +13 -0
- data/lib/keisan/ast/exponent.rb +62 -8
- data/lib/keisan/ast/function.rb +37 -26
- data/lib/keisan/ast/functions/diff.rb +57 -0
- data/lib/keisan/ast/functions/if.rb +47 -0
- data/lib/keisan/ast/indexing.rb +44 -4
- data/lib/keisan/ast/list.rb +4 -0
- data/lib/keisan/ast/literal.rb +8 -0
- data/lib/keisan/ast/logical_and.rb +9 -4
- data/lib/keisan/ast/logical_equal.rb +4 -4
- data/lib/keisan/ast/logical_greater_than.rb +4 -4
- data/lib/keisan/ast/logical_greater_than_or_equal_to.rb +4 -4
- data/lib/keisan/ast/logical_less_than.rb +4 -4
- data/lib/keisan/ast/logical_less_than_or_equal_to.rb +4 -4
- data/lib/keisan/ast/logical_not_equal.rb +4 -4
- data/lib/keisan/ast/logical_operator.rb +0 -3
- data/lib/keisan/ast/logical_or.rb +10 -5
- data/lib/keisan/ast/modulo.rb +4 -4
- data/lib/keisan/ast/node.rb +132 -20
- data/lib/keisan/ast/null.rb +1 -1
- data/lib/keisan/ast/number.rb +172 -1
- data/lib/keisan/ast/operator.rb +66 -8
- data/lib/keisan/ast/parent.rb +50 -0
- data/lib/keisan/ast/plus.rb +38 -4
- data/lib/keisan/ast/string.rb +10 -1
- data/lib/keisan/ast/times.rb +47 -4
- data/lib/keisan/ast/unary_bitwise_not.rb +9 -1
- data/lib/keisan/ast/unary_identity.rb +18 -1
- data/lib/keisan/ast/unary_inverse.rb +35 -1
- data/lib/keisan/ast/unary_logical_not.rb +5 -1
- data/lib/keisan/ast/unary_minus.rb +31 -1
- data/lib/keisan/ast/unary_operator.rb +29 -1
- data/lib/keisan/ast/unary_plus.rb +14 -1
- data/lib/keisan/ast/variable.rb +44 -0
- data/lib/keisan/calculator.rb +2 -2
- data/lib/keisan/context.rb +32 -16
- data/lib/keisan/evaluator.rb +10 -65
- data/lib/keisan/exceptions.rb +2 -0
- data/lib/keisan/function.rb +11 -5
- data/lib/keisan/function_definition_context.rb +34 -0
- data/lib/keisan/functions/default_registry.rb +13 -5
- data/lib/keisan/functions/diff.rb +82 -0
- data/lib/keisan/functions/expression_function.rb +63 -0
- data/lib/keisan/functions/if.rb +62 -0
- data/lib/keisan/functions/proc_function.rb +52 -0
- data/lib/keisan/functions/rand.rb +1 -1
- data/lib/keisan/functions/registry.rb +20 -10
- data/lib/keisan/functions/replace.rb +49 -0
- data/lib/keisan/functions/sample.rb +1 -1
- data/lib/keisan/parser.rb +13 -1
- data/lib/keisan/parsing/assignment.rb +9 -0
- data/lib/keisan/parsing/operator.rb +8 -0
- data/lib/keisan/parsing/unary_operator.rb +1 -1
- data/lib/keisan/tokenizer.rb +1 -0
- data/lib/keisan/tokens/assignment.rb +15 -0
- data/lib/keisan/variables/default_registry.rb +4 -4
- data/lib/keisan/variables/registry.rb +17 -8
- data/lib/keisan/version.rb +1 -1
- metadata +15 -3
- 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
|
data/lib/keisan/ast/indexing.rb
CHANGED
@@ -1,15 +1,55 @@
|
|
1
1
|
module Keisan
|
2
2
|
module AST
|
3
3
|
class Indexing < UnaryOperator
|
4
|
-
attr_reader :
|
4
|
+
attr_reader :indexes
|
5
5
|
|
6
|
-
def initialize(child,
|
6
|
+
def initialize(child, indexes = [])
|
7
7
|
@children = [child]
|
8
|
-
@
|
8
|
+
@indexes = indexes
|
9
9
|
end
|
10
10
|
|
11
11
|
def value(context = nil)
|
12
|
-
return
|
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
|
data/lib/keisan/ast/list.rb
CHANGED
data/lib/keisan/ast/literal.rb
CHANGED
@@ -1,17 +1,22 @@
|
|
1
1
|
module Keisan
|
2
2
|
module AST
|
3
3
|
class LogicalAnd < LogicalOperator
|
4
|
-
def
|
5
|
-
|
4
|
+
def self.symbol
|
5
|
+
:"&&"
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
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,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
|
data/lib/keisan/ast/modulo.rb
CHANGED
@@ -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], &:%)
|
data/lib/keisan/ast/node.rb
CHANGED
@@ -6,29 +6,141 @@ module Keisan
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def unbound_variables(context = nil)
|
9
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|