keisan 0.7.0 → 0.8.4
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 +5 -5
- data/.travis.yml +6 -3
- data/README.md +47 -3
- data/keisan.gemspec +5 -5
- data/lib/keisan.rb +9 -3
- data/lib/keisan/ast.rb +25 -0
- data/lib/keisan/ast/bitwise_left_shift.rb +17 -0
- data/lib/keisan/ast/bitwise_right_shift.rb +17 -0
- data/lib/keisan/ast/block.rb +4 -0
- data/lib/keisan/ast/boolean.rb +1 -1
- data/lib/keisan/ast/builder.rb +2 -2
- data/lib/keisan/ast/cell.rb +10 -0
- data/lib/keisan/ast/date.rb +23 -0
- data/lib/keisan/ast/date_time_methods.rb +75 -0
- data/lib/keisan/ast/function.rb +9 -0
- data/lib/keisan/ast/function_assignment.rb +16 -6
- data/lib/keisan/ast/hash.rb +4 -0
- data/lib/keisan/ast/logical_and.rb +20 -3
- data/lib/keisan/ast/logical_equal.rb +6 -5
- data/lib/keisan/ast/logical_greater_than.rb +6 -4
- data/lib/keisan/ast/logical_greater_than_or_equal_to.rb +6 -4
- data/lib/keisan/ast/logical_less_than.rb +6 -4
- data/lib/keisan/ast/logical_less_than_or_equal_to.rb +6 -4
- data/lib/keisan/ast/logical_not_equal.rb +6 -5
- data/lib/keisan/ast/logical_operator.rb +24 -0
- data/lib/keisan/ast/logical_or.rb +18 -1
- data/lib/keisan/ast/node.rb +25 -0
- data/lib/keisan/ast/number.rb +24 -0
- data/lib/keisan/ast/operator.rb +3 -1
- data/lib/keisan/ast/parent.rb +5 -1
- data/lib/keisan/ast/plus.rb +10 -0
- data/lib/keisan/ast/time.rb +23 -0
- data/lib/keisan/ast/unary_inverse.rb +1 -1
- data/lib/keisan/ast/unary_operator.rb +1 -1
- data/lib/keisan/ast/variable.rb +10 -9
- data/lib/keisan/calculator.rb +17 -3
- data/lib/keisan/context.rb +27 -10
- data/lib/keisan/evaluator.rb +16 -4
- data/lib/keisan/exceptions.rb +3 -0
- data/lib/keisan/function.rb +6 -0
- data/lib/keisan/functions/break.rb +11 -0
- data/lib/keisan/functions/cmath_function.rb +3 -1
- data/lib/keisan/functions/continue.rb +11 -0
- data/lib/keisan/functions/default_registry.rb +39 -0
- data/lib/keisan/functions/enumerable_function.rb +10 -2
- data/lib/keisan/functions/expression_function.rb +16 -9
- data/lib/keisan/functions/filter.rb +6 -0
- data/lib/keisan/functions/loop_control_flow_function.rb +22 -0
- data/lib/keisan/functions/map.rb +6 -0
- data/lib/keisan/functions/proc_function.rb +2 -2
- data/lib/keisan/functions/reduce.rb +5 -0
- data/lib/keisan/functions/replace.rb +6 -6
- data/lib/keisan/functions/while.rb +7 -1
- data/lib/keisan/parser.rb +7 -5
- data/lib/keisan/parsing/bitwise_left_shift.rb +9 -0
- data/lib/keisan/parsing/bitwise_right_shift.rb +9 -0
- data/lib/keisan/parsing/function.rb +1 -1
- data/lib/keisan/parsing/hash.rb +2 -2
- data/lib/keisan/string_and_group_parser.rb +229 -0
- data/lib/keisan/token.rb +1 -1
- data/lib/keisan/tokenizer.rb +20 -18
- data/lib/keisan/tokens/assignment.rb +3 -1
- data/lib/keisan/tokens/bitwise_shift.rb +23 -0
- data/lib/keisan/tokens/group.rb +1 -7
- data/lib/keisan/tokens/string.rb +2 -4
- data/lib/keisan/util.rb +19 -0
- data/lib/keisan/variables/default_registry.rb +2 -1
- data/lib/keisan/version.rb +1 -1
- metadata +40 -28
@@ -29,8 +29,7 @@ module Keisan
|
|
29
29
|
|
30
30
|
def evaluate
|
31
31
|
# Blocks might have local variable/function definitions, so skip check
|
32
|
-
verify_rhs_of_function_assignment_is_valid!
|
33
|
-
|
32
|
+
verify_rhs_of_function_assignment_is_valid!
|
34
33
|
context.register_function!(lhs.name, expression_function, local: local)
|
35
34
|
rhs
|
36
35
|
end
|
@@ -38,15 +37,26 @@ module Keisan
|
|
38
37
|
private
|
39
38
|
|
40
39
|
def verify_rhs_of_function_assignment_is_valid!
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
verify_unbound_functions!
|
41
|
+
verify_unbound_variables!
|
42
|
+
end
|
43
|
+
|
44
|
+
def verify_unbound_functions!
|
45
45
|
# Cannot have undefined functions unless allowed by context
|
46
46
|
unless context.allow_recursive || rhs.unbound_functions(context).empty?
|
47
47
|
raise Exceptions::InvalidExpression.new("Unbound function definitions are not allowed by current context")
|
48
48
|
end
|
49
49
|
end
|
50
|
+
|
51
|
+
def verify_unbound_variables!
|
52
|
+
# We allow unbound variables inside block statements, as they could be temporary
|
53
|
+
# variables assigned locally
|
54
|
+
return if rhs.is_a?(Block)
|
55
|
+
# Only variables that can appear are those that are arguments to the function
|
56
|
+
unless rhs.unbound_variables(context) <= Set.new(argument_names)
|
57
|
+
raise Exceptions::InvalidExpression.new("Unbound variables found in function definition")
|
58
|
+
end
|
59
|
+
end
|
50
60
|
end
|
51
61
|
end
|
52
62
|
end
|
data/lib/keisan/ast/hash.rb
CHANGED
@@ -5,18 +5,35 @@ module Keisan
|
|
5
5
|
:"&&"
|
6
6
|
end
|
7
7
|
|
8
|
+
def blank_value
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
8
12
|
def evaluate(context = nil)
|
9
|
-
|
13
|
+
short_circuit_do(:evaluate, context)
|
10
14
|
end
|
11
15
|
|
12
|
-
def
|
13
|
-
|
16
|
+
def simplify(context = nil)
|
17
|
+
short_circuit_do(:simplify, context)
|
14
18
|
end
|
15
19
|
|
16
20
|
def value(context = nil)
|
17
21
|
context ||= Context.new
|
18
22
|
children[0].value(context) && children[1].value(context)
|
19
23
|
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def short_circuit_do(method, context)
|
28
|
+
context ||= Context.new
|
29
|
+
lhs = children[0].send(method, context)
|
30
|
+
case lhs
|
31
|
+
when AST::Boolean
|
32
|
+
lhs.false? ? AST::Boolean.new(false) : children[1].send(method, context)
|
33
|
+
else
|
34
|
+
lhs.and(children[1].send(method, context))
|
35
|
+
end
|
36
|
+
end
|
20
37
|
end
|
21
38
|
end
|
22
39
|
end
|
@@ -5,13 +5,14 @@ module Keisan
|
|
5
5
|
:"=="
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
protected
|
9
|
+
|
10
|
+
def value_operator
|
11
|
+
:==
|
10
12
|
end
|
11
13
|
|
12
|
-
def
|
13
|
-
|
14
|
-
children[0].value(context) == children[1].value(context)
|
14
|
+
def operator
|
15
|
+
:equal
|
15
16
|
end
|
16
17
|
end
|
17
18
|
end
|
@@ -5,12 +5,14 @@ module Keisan
|
|
5
5
|
:">"
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
protected
|
9
|
+
|
10
|
+
def value_operator
|
11
|
+
:>
|
10
12
|
end
|
11
13
|
|
12
|
-
def
|
13
|
-
|
14
|
+
def operator
|
15
|
+
:>
|
14
16
|
end
|
15
17
|
end
|
16
18
|
end
|
@@ -5,12 +5,14 @@ module Keisan
|
|
5
5
|
:">="
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
protected
|
9
|
+
|
10
|
+
def value_operator
|
11
|
+
:>=
|
10
12
|
end
|
11
13
|
|
12
|
-
def
|
13
|
-
|
14
|
+
def operator
|
15
|
+
:>=
|
14
16
|
end
|
15
17
|
end
|
16
18
|
end
|
@@ -5,12 +5,14 @@ module Keisan
|
|
5
5
|
:"<"
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
protected
|
9
|
+
|
10
|
+
def value_operator
|
11
|
+
:<
|
10
12
|
end
|
11
13
|
|
12
|
-
def
|
13
|
-
|
14
|
+
def operator
|
15
|
+
:<
|
14
16
|
end
|
15
17
|
end
|
16
18
|
end
|
@@ -5,12 +5,14 @@ module Keisan
|
|
5
5
|
:"<="
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
protected
|
9
|
+
|
10
|
+
def value_operator
|
11
|
+
:<=
|
10
12
|
end
|
11
13
|
|
12
|
-
def
|
13
|
-
|
14
|
+
def operator
|
15
|
+
:<=
|
14
16
|
end
|
15
17
|
end
|
16
18
|
end
|
@@ -5,13 +5,14 @@ module Keisan
|
|
5
5
|
:"!="
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
protected
|
9
|
+
|
10
|
+
def value_operator
|
11
|
+
:!=
|
10
12
|
end
|
11
13
|
|
12
|
-
def
|
13
|
-
|
14
|
-
children[0].value(context) != children[1].value(context)
|
14
|
+
def operator
|
15
|
+
:not_equal
|
15
16
|
end
|
16
17
|
end
|
17
18
|
end
|
@@ -1,6 +1,30 @@
|
|
1
1
|
module Keisan
|
2
2
|
module AST
|
3
3
|
class LogicalOperator < Operator
|
4
|
+
def evaluate(context = nil)
|
5
|
+
context ||= Context.new
|
6
|
+
children[0].evaluate(context).send(operator, children[1].evaluate(context))
|
7
|
+
end
|
8
|
+
|
9
|
+
def simplify(context = nil)
|
10
|
+
context ||= Context.new
|
11
|
+
children[0].simplify(context).send(operator, children[1].simplify(context))
|
12
|
+
end
|
13
|
+
|
14
|
+
def value(context=nil)
|
15
|
+
context ||= Context.new
|
16
|
+
children[0].value(context).send(value_operator, children[1].value(context))
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def value_operator
|
22
|
+
raise Exceptions::NotImplementedError.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def operator
|
26
|
+
raise Exceptions::NotImplementedError.new
|
27
|
+
end
|
4
28
|
end
|
5
29
|
end
|
6
30
|
end
|
@@ -10,13 +10,30 @@ module Keisan
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def evaluate(context = nil)
|
13
|
-
|
13
|
+
short_circuit_do(:evaluate, context)
|
14
|
+
end
|
15
|
+
|
16
|
+
def simplify(context = nil)
|
17
|
+
short_circuit_do(:simplify, context)
|
14
18
|
end
|
15
19
|
|
16
20
|
def value(context = nil)
|
17
21
|
context ||= Context.new
|
18
22
|
children[0].value(context) || children[1].value(context)
|
19
23
|
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def short_circuit_do(method, context)
|
28
|
+
context ||= Context.new
|
29
|
+
lhs = children[0].send(method, context)
|
30
|
+
case lhs
|
31
|
+
when AST::Boolean
|
32
|
+
lhs.true? ? AST::Boolean.new(true) : children[1].send(method, context)
|
33
|
+
else
|
34
|
+
lhs.or(children[1].send(method, context))
|
35
|
+
end
|
36
|
+
end
|
20
37
|
end
|
21
38
|
end
|
22
39
|
end
|
data/lib/keisan/ast/node.rb
CHANGED
@@ -37,6 +37,15 @@ module Keisan
|
|
37
37
|
value(context)
|
38
38
|
end
|
39
39
|
|
40
|
+
def contains_a?(klass)
|
41
|
+
case klass
|
42
|
+
when Array
|
43
|
+
klass.any? {|k| is_a?(k) }
|
44
|
+
else
|
45
|
+
is_a?(klass)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
40
49
|
def evaluate_assignments(context = nil)
|
41
50
|
self
|
42
51
|
end
|
@@ -45,10 +54,18 @@ module Keisan
|
|
45
54
|
raise Exceptions::NonDifferentiableError.new
|
46
55
|
end
|
47
56
|
|
57
|
+
def differentiated(variable, context = nil)
|
58
|
+
deep_dup.differentiate(variable, context)
|
59
|
+
end
|
60
|
+
|
48
61
|
def replace(variable, replacement)
|
49
62
|
self
|
50
63
|
end
|
51
64
|
|
65
|
+
def replaced(variable, replacement)
|
66
|
+
deep_dup.replace(variable, replacement)
|
67
|
+
end
|
68
|
+
|
52
69
|
def coerce(other)
|
53
70
|
[other.to_node, self]
|
54
71
|
end
|
@@ -132,6 +149,14 @@ module Keisan
|
|
132
149
|
BitwiseOr.new([self, other.to_node])
|
133
150
|
end
|
134
151
|
|
152
|
+
def <<(other)
|
153
|
+
BitwiseLeftShift.new([self, other.to_node])
|
154
|
+
end
|
155
|
+
|
156
|
+
def >>(other)
|
157
|
+
BitwiseRightShift.new([self, other.to_node])
|
158
|
+
end
|
159
|
+
|
135
160
|
def >(other)
|
136
161
|
LogicalGreaterThan.new([self, other.to_node])
|
137
162
|
end
|
data/lib/keisan/ast/number.rb
CHANGED
@@ -24,6 +24,10 @@ module Keisan
|
|
24
24
|
case other
|
25
25
|
when Number
|
26
26
|
Number.new(value + other.value)
|
27
|
+
when Date
|
28
|
+
Date.new(other.value + value)
|
29
|
+
when Time
|
30
|
+
Time.new(other.value + value)
|
27
31
|
else
|
28
32
|
super
|
29
33
|
end
|
@@ -107,6 +111,26 @@ module Keisan
|
|
107
111
|
end
|
108
112
|
end
|
109
113
|
|
114
|
+
def <<(other)
|
115
|
+
other = other.to_node
|
116
|
+
case other
|
117
|
+
when Number
|
118
|
+
Number.new(value << other.value)
|
119
|
+
else
|
120
|
+
super
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def >>(other)
|
125
|
+
other = other.to_node
|
126
|
+
case other
|
127
|
+
when Number
|
128
|
+
Number.new(value >> other.value)
|
129
|
+
else
|
130
|
+
super
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
110
134
|
def >(other)
|
111
135
|
other = other.to_node
|
112
136
|
case other
|
data/lib/keisan/ast/operator.rb
CHANGED
@@ -13,6 +13,8 @@ module Keisan
|
|
13
13
|
"%": [2, 85, :left], # Modulo
|
14
14
|
"+": [2, 80, :left], # Plus
|
15
15
|
# "-": [2, 80, :left], # Minus
|
16
|
+
"<<": [2, 75, :left], # Bitwise left shift
|
17
|
+
">>": [2, 75, :left], # Bitwise right shift
|
16
18
|
"&": [2, 70, :left], # Bitwise and
|
17
19
|
"^": [2, 65, :left], # Bitwise xor
|
18
20
|
"|": [2, 65, :left], # Bitwise or
|
@@ -41,7 +43,7 @@ module Keisan
|
|
41
43
|
raise Exceptions::ASTError.new("Mismatch of children and operators")
|
42
44
|
end
|
43
45
|
|
44
|
-
children = Array
|
46
|
+
children = Array(children)
|
45
47
|
super(children)
|
46
48
|
|
47
49
|
@parsing_operators = parsing_operators
|
data/lib/keisan/ast/parent.rb
CHANGED
@@ -4,7 +4,7 @@ module Keisan
|
|
4
4
|
attr_reader :children
|
5
5
|
|
6
6
|
def initialize(children = [])
|
7
|
-
children = Array
|
7
|
+
children = Array(children).map do |child|
|
8
8
|
child.is_a?(Cell) ? child : child.to_node
|
9
9
|
end
|
10
10
|
raise Exceptions::InternalError.new unless children.is_a?(Array)
|
@@ -25,6 +25,10 @@ module Keisan
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
def contains_a?(klass)
|
29
|
+
super || children.any? {|child| child.contains_a?(klass) }
|
30
|
+
end
|
31
|
+
|
28
32
|
def freeze
|
29
33
|
children.each(&:freeze)
|
30
34
|
super
|
data/lib/keisan/ast/plus.rb
CHANGED
@@ -22,6 +22,10 @@ module Keisan
|
|
22
22
|
# Special case of array concatenation
|
23
23
|
elsif children_values.all? {|child| child.is_a?(::Array)}
|
24
24
|
children_values.inject([], &:+)
|
25
|
+
elsif children_values.one? {|child| child.is_a?(::Date)}
|
26
|
+
date_time_plus(children_values, ::Date)
|
27
|
+
elsif children_values.one? {|child| child.is_a?(::Time)}
|
28
|
+
date_time_plus(children_values, ::Time)
|
25
29
|
else
|
26
30
|
children_values.inject(0, &:+)
|
27
31
|
end.to_node.value(context)
|
@@ -77,6 +81,12 @@ module Keisan
|
|
77
81
|
|
78
82
|
private
|
79
83
|
|
84
|
+
def date_time_plus(elements, klass)
|
85
|
+
date_time = elements.select {|child| child.is_a?(klass)}.first
|
86
|
+
others = elements.select {|child| !child.is_a?(klass)}
|
87
|
+
date_time + others.inject(0, &:+)
|
88
|
+
end
|
89
|
+
|
80
90
|
def convert_minus_to_plus!
|
81
91
|
@parsing_operators.each.with_index do |parsing_operator, index|
|
82
92
|
if parsing_operator.is_a?(Parsing::Minus)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative "date_time_methods"
|
2
|
+
|
3
|
+
module Keisan
|
4
|
+
module AST
|
5
|
+
class Time < ConstantLiteral
|
6
|
+
include DateTimeMethods
|
7
|
+
|
8
|
+
attr_reader :time
|
9
|
+
|
10
|
+
def initialize(time)
|
11
|
+
@time = time
|
12
|
+
end
|
13
|
+
|
14
|
+
def value(context = nil)
|
15
|
+
time
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
value.strftime("%Y-%m-%d %H:%M:%S")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|