keisan 0.5.0 → 0.6.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 +49 -1
- data/keisan.gemspec +1 -0
- data/lib/keisan.rb +30 -0
- data/lib/keisan/ast/assignment.rb +44 -17
- data/lib/keisan/ast/block.rb +60 -0
- data/lib/keisan/ast/boolean.rb +5 -5
- data/lib/keisan/ast/builder.rb +10 -207
- data/lib/keisan/ast/cell.rb +60 -0
- data/lib/keisan/ast/constant_literal.rb +9 -0
- data/lib/keisan/ast/exponent.rb +6 -6
- data/lib/keisan/ast/function.rb +12 -8
- data/lib/keisan/ast/indexing.rb +25 -15
- data/lib/keisan/ast/line_builder.rb +230 -0
- data/lib/keisan/ast/list.rb +28 -1
- data/lib/keisan/ast/literal.rb +0 -8
- data/lib/keisan/ast/logical_and.rb +1 -1
- data/lib/keisan/ast/logical_or.rb +1 -1
- data/lib/keisan/ast/multi_line.rb +28 -0
- data/lib/keisan/ast/node.rb +32 -24
- data/lib/keisan/ast/number.rb +31 -31
- data/lib/keisan/ast/operator.rb +12 -4
- data/lib/keisan/ast/parent.rb +4 -4
- data/lib/keisan/ast/plus.rb +10 -10
- data/lib/keisan/ast/string.rb +3 -3
- data/lib/keisan/ast/times.rb +8 -8
- data/lib/keisan/ast/unary_identity.rb +1 -1
- data/lib/keisan/ast/unary_inverse.rb +7 -7
- data/lib/keisan/ast/unary_minus.rb +5 -5
- data/lib/keisan/ast/unary_operator.rb +2 -2
- data/lib/keisan/ast/unary_plus.rb +2 -2
- data/lib/keisan/ast/variable.rb +26 -10
- data/lib/keisan/context.rb +5 -5
- data/lib/keisan/evaluator.rb +15 -8
- data/lib/keisan/function.rb +24 -6
- data/lib/keisan/functions/cbrt.rb +1 -1
- data/lib/keisan/functions/cos.rb +1 -1
- data/lib/keisan/functions/cosh.rb +1 -1
- data/lib/keisan/functions/cot.rb +1 -1
- data/lib/keisan/functions/coth.rb +1 -1
- data/lib/keisan/functions/csc.rb +1 -1
- data/lib/keisan/functions/csch.rb +1 -1
- data/lib/keisan/functions/default_registry.rb +53 -74
- data/lib/keisan/functions/diff.rb +18 -14
- data/lib/keisan/functions/erf.rb +15 -0
- data/lib/keisan/functions/exp.rb +1 -1
- data/lib/keisan/functions/expression_function.rb +15 -21
- data/lib/keisan/functions/filter.rb +13 -15
- data/lib/keisan/functions/if.rb +14 -20
- data/lib/keisan/functions/let.rb +36 -0
- data/lib/keisan/functions/map.rb +11 -13
- data/lib/keisan/functions/math_function.rb +2 -2
- data/lib/keisan/functions/proc_function.rb +10 -6
- data/lib/keisan/functions/rand.rb +2 -1
- data/lib/keisan/functions/range.rb +74 -0
- data/lib/keisan/functions/reduce.rb +12 -14
- data/lib/keisan/functions/registry.rb +7 -7
- data/lib/keisan/functions/replace.rb +8 -8
- data/lib/keisan/functions/sample.rb +2 -1
- data/lib/keisan/functions/sec.rb +1 -1
- data/lib/keisan/functions/sech.rb +1 -1
- data/lib/keisan/functions/sin.rb +1 -1
- data/lib/keisan/functions/sinh.rb +1 -1
- data/lib/keisan/functions/sqrt.rb +1 -1
- data/lib/keisan/functions/tan.rb +1 -1
- data/lib/keisan/functions/tanh.rb +1 -1
- data/lib/keisan/functions/while.rb +46 -0
- data/lib/keisan/parser.rb +121 -79
- data/lib/keisan/parsing/assignment.rb +1 -1
- data/lib/keisan/parsing/bitwise_and.rb +1 -1
- data/lib/keisan/parsing/bitwise_not.rb +1 -1
- data/lib/keisan/parsing/bitwise_not_not.rb +1 -1
- data/lib/keisan/parsing/bitwise_or.rb +1 -1
- data/lib/keisan/parsing/bitwise_xor.rb +1 -1
- data/lib/keisan/parsing/curly_group.rb +6 -0
- data/lib/keisan/parsing/divide.rb +1 -1
- data/lib/keisan/parsing/exponent.rb +1 -1
- data/lib/keisan/parsing/function.rb +1 -1
- data/lib/keisan/parsing/group.rb +1 -1
- data/lib/keisan/parsing/indexing.rb +1 -1
- data/lib/keisan/parsing/line_separator.rb +6 -0
- data/lib/keisan/parsing/logical_and.rb +1 -1
- data/lib/keisan/parsing/logical_equal.rb +1 -1
- data/lib/keisan/parsing/logical_greater_than.rb +1 -1
- data/lib/keisan/parsing/logical_greater_than_or_equal_to.rb +1 -1
- data/lib/keisan/parsing/logical_less_than.rb +1 -1
- data/lib/keisan/parsing/logical_less_than_or_equal_to.rb +1 -1
- data/lib/keisan/parsing/logical_not.rb +1 -1
- data/lib/keisan/parsing/logical_not_equal.rb +1 -1
- data/lib/keisan/parsing/logical_not_not.rb +1 -1
- data/lib/keisan/parsing/logical_or.rb +1 -1
- data/lib/keisan/parsing/minus.rb +1 -1
- data/lib/keisan/parsing/modulo.rb +1 -1
- data/lib/keisan/parsing/operator.rb +1 -1
- data/lib/keisan/parsing/plus.rb +1 -1
- data/lib/keisan/parsing/times.rb +1 -1
- data/lib/keisan/parsing/unary_minus.rb +1 -1
- data/lib/keisan/parsing/unary_operator.rb +1 -1
- data/lib/keisan/parsing/unary_plus.rb +1 -1
- data/lib/keisan/repl.rb +1 -1
- data/lib/keisan/tokenizer.rb +4 -9
- data/lib/keisan/tokens/group.rb +3 -1
- data/lib/keisan/tokens/line_separator.rb +11 -0
- data/lib/keisan/variables/default_registry.rb +0 -5
- data/lib/keisan/variables/registry.rb +7 -7
- data/lib/keisan/version.rb +1 -1
- metadata +27 -2
@@ -0,0 +1,60 @@
|
|
1
|
+
module Keisan
|
2
|
+
module AST
|
3
|
+
class Cell < Node
|
4
|
+
attr_accessor :node
|
5
|
+
|
6
|
+
def initialize(node)
|
7
|
+
@node = node
|
8
|
+
end
|
9
|
+
|
10
|
+
def unbound_variables(context = nil)
|
11
|
+
node.unbound_variables(context)
|
12
|
+
end
|
13
|
+
|
14
|
+
def unbound_functions(context = nil)
|
15
|
+
node.unbound_functions(context)
|
16
|
+
end
|
17
|
+
|
18
|
+
def deep_dup
|
19
|
+
dupped = dup
|
20
|
+
dupped.instance_variable_set(
|
21
|
+
:@node,
|
22
|
+
dupped.node.deep_dup
|
23
|
+
)
|
24
|
+
dupped
|
25
|
+
end
|
26
|
+
|
27
|
+
def value(context = nil)
|
28
|
+
node.value(context)
|
29
|
+
end
|
30
|
+
|
31
|
+
def evaluate(context = nil)
|
32
|
+
node.evaluate(context)
|
33
|
+
end
|
34
|
+
|
35
|
+
def simplify(context = nil)
|
36
|
+
node.simplify(context)
|
37
|
+
end
|
38
|
+
|
39
|
+
def evaluate_assignments(context = nil)
|
40
|
+
node.evaluate_assignments(context)
|
41
|
+
end
|
42
|
+
|
43
|
+
def differentiate(variable, context = nil)
|
44
|
+
node.differentiate(variable, context)
|
45
|
+
end
|
46
|
+
|
47
|
+
def replace(variable, replacement)
|
48
|
+
node.replace(variable, replacement)
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_s
|
52
|
+
node.to_s
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_node
|
56
|
+
node
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/keisan/ast/exponent.rb
CHANGED
@@ -19,20 +19,20 @@ module Keisan
|
|
19
19
|
child ** total
|
20
20
|
end
|
21
21
|
|
22
|
-
unless reduced.is_a?(
|
22
|
+
unless reduced.is_a?(Exponent)
|
23
23
|
return reduced.simplify(context)
|
24
24
|
end
|
25
25
|
|
26
26
|
if reduced.children.count != 2
|
27
|
-
raise
|
27
|
+
raise Exceptions::InternalError.new("Exponent should be binary")
|
28
28
|
end
|
29
29
|
@children = reduced.children
|
30
30
|
|
31
|
-
if children[1].is_a?(
|
31
|
+
if children[1].is_a?(Number) && children[1].value(context) == 1
|
32
32
|
return children[0]
|
33
33
|
end
|
34
34
|
|
35
|
-
if children.all? {|child| child.is_a?(
|
35
|
+
if children.all? {|child| child.is_a?(Number)}
|
36
36
|
(children[0] ** children[1]).simplify(context)
|
37
37
|
else
|
38
38
|
self
|
@@ -57,7 +57,7 @@ module Keisan
|
|
57
57
|
exponent = children[1].simplified(context)
|
58
58
|
|
59
59
|
# Special simple case where exponent is a pure number
|
60
|
-
if exponent.is_a?(
|
60
|
+
if exponent.is_a?(Number)
|
61
61
|
return (exponent * base.differentiate(variable, context) * base ** (exponent -1)).simplify(context)
|
62
62
|
end
|
63
63
|
|
@@ -65,7 +65,7 @@ module Keisan
|
|
65
65
|
exponent_diff = exponent.differentiate(variable, context)
|
66
66
|
|
67
67
|
res = base ** exponent * (
|
68
|
-
exponent_diff *
|
68
|
+
exponent_diff * Function.new([base], "log") +
|
69
69
|
base_diff * exponent / base
|
70
70
|
)
|
71
71
|
res.simplify(context)
|
data/lib/keisan/ast/function.rb
CHANGED
@@ -9,12 +9,12 @@ module Keisan
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def value(context = nil)
|
12
|
-
context ||=
|
12
|
+
context ||= Context.new
|
13
13
|
function_from_context(context).value(self, context)
|
14
14
|
end
|
15
15
|
|
16
16
|
def unbound_functions(context = nil)
|
17
|
-
context ||=
|
17
|
+
context ||= Context.new
|
18
18
|
|
19
19
|
functions = children.inject(Set.new) do |res, child|
|
20
20
|
res | child.unbound_functions(context)
|
@@ -23,8 +23,12 @@ module Keisan
|
|
23
23
|
context.has_function?(name) ? functions : functions | Set.new([name])
|
24
24
|
end
|
25
25
|
|
26
|
+
def evaluate_assignments(context = nil)
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
26
30
|
def function_defined?(context = nil)
|
27
|
-
context ||=
|
31
|
+
context ||= Context.new
|
28
32
|
context.has_function?(name)
|
29
33
|
end
|
30
34
|
|
@@ -34,7 +38,7 @@ module Keisan
|
|
34
38
|
|
35
39
|
def ==(other)
|
36
40
|
case other
|
37
|
-
when
|
41
|
+
when Function
|
38
42
|
name == other.name && super
|
39
43
|
else
|
40
44
|
false
|
@@ -42,7 +46,7 @@ module Keisan
|
|
42
46
|
end
|
43
47
|
|
44
48
|
def evaluate(context = nil)
|
45
|
-
context ||=
|
49
|
+
context ||= Context.new
|
46
50
|
|
47
51
|
if function_defined?(context)
|
48
52
|
function_from_context(context).evaluate(self, context)
|
@@ -71,12 +75,12 @@ module Keisan
|
|
71
75
|
function = function_from_context(context)
|
72
76
|
function.differentiate(self, variable, context)
|
73
77
|
|
74
|
-
rescue
|
78
|
+
rescue Exceptions::UndefinedFunctionError, Exceptions::NotImplementedError
|
75
79
|
unless unbound_variables(context).include?(variable.name)
|
76
|
-
return
|
80
|
+
return Number.new(0)
|
77
81
|
end
|
78
82
|
|
79
|
-
|
83
|
+
self.class.new([self, variable], "diff")
|
80
84
|
end
|
81
85
|
end
|
82
86
|
end
|
data/lib/keisan/ast/indexing.rb
CHANGED
@@ -16,19 +16,20 @@ module Keisan
|
|
16
16
|
"(#{child.to_s})[#{indexes.map(&:to_s).join(',')}]"
|
17
17
|
end
|
18
18
|
|
19
|
+
def evaluate_assignments(context = nil)
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
19
23
|
def evaluate(context = nil)
|
20
|
-
context ||=
|
24
|
+
context ||= Context.new
|
21
25
|
@children = children.map {|child| child.evaluate(context)}
|
22
26
|
@indexes = indexes.map {|index| index.evaluate(context)}
|
23
27
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
28
|
+
if list = extract_list
|
29
|
+
list.children[@indexes.first.value(context)]
|
30
|
+
else
|
31
|
+
self
|
29
32
|
end
|
30
|
-
|
31
|
-
self
|
32
33
|
end
|
33
34
|
|
34
35
|
def simplify(context = nil)
|
@@ -37,20 +38,29 @@ module Keisan
|
|
37
38
|
@indexes = indexes.map {|index| index.simplify(context)}
|
38
39
|
@children = [child.simplify(context)]
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
end
|
41
|
+
if list = extract_list
|
42
|
+
Cell.new(list.children[@indexes.first.value(context)].simplify(context))
|
43
|
+
else
|
44
|
+
self
|
45
45
|
end
|
46
|
-
|
47
|
-
self
|
48
46
|
end
|
49
47
|
|
50
48
|
def replace(variable, replacement)
|
51
49
|
super
|
52
50
|
@indexes = indexes.map {|index| index.replace(variable, replacement)}
|
53
51
|
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def extract_list
|
56
|
+
if child.is_a?(List)
|
57
|
+
child
|
58
|
+
elsif child.is_a?(Cell) && child.node.is_a?(List)
|
59
|
+
child.node
|
60
|
+
else
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
end
|
54
64
|
end
|
55
65
|
end
|
56
66
|
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
module Keisan
|
2
|
+
module AST
|
3
|
+
class LineBuilder
|
4
|
+
# Build from parser
|
5
|
+
def initialize(components)
|
6
|
+
@components = components
|
7
|
+
@nodes = components_to_basic_nodes(@components)
|
8
|
+
|
9
|
+
# Negative means not an operator
|
10
|
+
@priorities = @nodes.map {|node| node.is_a?(Keisan::Parsing::Operator) ? node.priority : -1}
|
11
|
+
|
12
|
+
consume_operators!
|
13
|
+
|
14
|
+
case @nodes.count
|
15
|
+
when 0
|
16
|
+
# Empty string, set to just Null
|
17
|
+
@nodes = [Keisan::AST::Null.new]
|
18
|
+
when 1
|
19
|
+
# Good
|
20
|
+
else
|
21
|
+
raise Keisan::Exceptions::ASTError.new("Should end up with a single node")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def node
|
26
|
+
@nodes.first
|
27
|
+
end
|
28
|
+
|
29
|
+
def ast
|
30
|
+
node
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# Array of AST elements, and Parsing operators
|
36
|
+
def components_to_basic_nodes(components)
|
37
|
+
nodes_components = []
|
38
|
+
|
39
|
+
components.each do |component|
|
40
|
+
if component.is_a?(Keisan::Parsing::LineSeparator)
|
41
|
+
nodes_components << [component]
|
42
|
+
elsif nodes_components.empty? || nodes_components.last.last.is_a?(Keisan::Parsing::LineSeparator)
|
43
|
+
nodes_components << [component]
|
44
|
+
else
|
45
|
+
is_operator = [nodes_components.last.last.is_a?(Keisan::Parsing::Operator), component.is_a?(Keisan::Parsing::Operator)]
|
46
|
+
|
47
|
+
if is_operator.first == is_operator.last
|
48
|
+
nodes_components.last << component
|
49
|
+
else
|
50
|
+
nodes_components << [component]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
nodes_components.inject([]) do |nodes, node_or_component_group|
|
56
|
+
if node_or_component_group.first.is_a?(Keisan::Parsing::Operator)
|
57
|
+
node_or_component_group.each do |component|
|
58
|
+
nodes << component
|
59
|
+
end
|
60
|
+
else
|
61
|
+
nodes << node_from_components(node_or_component_group)
|
62
|
+
end
|
63
|
+
|
64
|
+
nodes
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def node_from_components(components)
|
69
|
+
node, postfix_components = *node_postfixes(components)
|
70
|
+
# Apply postfix operators
|
71
|
+
postfix_components.each do |postfix_component|
|
72
|
+
node = apply_postfix_component_to_node(postfix_component, node)
|
73
|
+
end
|
74
|
+
|
75
|
+
node
|
76
|
+
end
|
77
|
+
|
78
|
+
def apply_postfix_component_to_node(postfix_component, node)
|
79
|
+
case postfix_component
|
80
|
+
when Keisan::Parsing::Indexing
|
81
|
+
postfix_component.node_class.new(
|
82
|
+
node,
|
83
|
+
postfix_component.arguments.map {|parsing_argument|
|
84
|
+
Builder.new(components: parsing_argument.components).node
|
85
|
+
}
|
86
|
+
)
|
87
|
+
when Keisan::Parsing::DotWord
|
88
|
+
Keisan::AST::Function.new(
|
89
|
+
[node],
|
90
|
+
postfix_component.name
|
91
|
+
)
|
92
|
+
when Keisan::Parsing::DotOperator
|
93
|
+
Keisan::AST::Function.new(
|
94
|
+
[node] + postfix_component.arguments.map {|parsing_argument|
|
95
|
+
Builder.new(components: parsing_argument.components).node
|
96
|
+
},
|
97
|
+
postfix_component.name
|
98
|
+
)
|
99
|
+
else
|
100
|
+
raise Keisan::Exceptions::ASTError.new("Invalid postfix component #{postfix_component}")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns an array of the form
|
105
|
+
# [node, postfix_operators]
|
106
|
+
# middle_node is the main node which will be modified by prefix and postfix operators
|
107
|
+
# postfix_operators is an array of Keisan::Parsing::Indexing, DotWord, and DotOperator objects
|
108
|
+
def node_postfixes(components)
|
109
|
+
index_of_postfix_components = components.map.with_index {|c,i| [c,i]}.select {|c,i|
|
110
|
+
c.is_a?(Keisan::Parsing::Indexing) || c.is_a?(Keisan::Parsing::DotWord) || c.is_a?(Keisan::Parsing::DotOperator)
|
111
|
+
}.map(&:last)
|
112
|
+
unless index_of_postfix_components.reverse.map.with_index.all? {|i,j| i + j == components.size - 1 }
|
113
|
+
raise Keisan::Exceptions::ASTError.new("postfix components must be in back")
|
114
|
+
end
|
115
|
+
|
116
|
+
num_postfix = index_of_postfix_components.size
|
117
|
+
|
118
|
+
unless num_postfix + 1 == components.size
|
119
|
+
raise Keisan::Exceptions::ASTError.new("have too many components")
|
120
|
+
end
|
121
|
+
|
122
|
+
[
|
123
|
+
node_of_component(components[0]),
|
124
|
+
index_of_postfix_components.map {|i| components[i]}
|
125
|
+
]
|
126
|
+
end
|
127
|
+
|
128
|
+
def node_of_component(component)
|
129
|
+
case component
|
130
|
+
when Parsing::Number
|
131
|
+
AST::Number.new(component.value)
|
132
|
+
when Parsing::String
|
133
|
+
AST::String.new(component.value)
|
134
|
+
when Parsing::Null
|
135
|
+
AST::Null.new
|
136
|
+
when Parsing::Variable
|
137
|
+
AST::Variable.new(component.name)
|
138
|
+
when Parsing::Boolean
|
139
|
+
AST::Boolean.new(component.value)
|
140
|
+
when Parsing::List
|
141
|
+
AST::List.new(
|
142
|
+
component.arguments.map {|parsing_argument|
|
143
|
+
Builder.new(components: parsing_argument.components).node
|
144
|
+
}
|
145
|
+
)
|
146
|
+
when Parsing::RoundGroup
|
147
|
+
Builder.new(components: component.components).node
|
148
|
+
when Parsing::CurlyGroup
|
149
|
+
Block.new(Builder.new(components: component.components).node)
|
150
|
+
when Parsing::Function
|
151
|
+
AST::Function.new(
|
152
|
+
component.arguments.map {|parsing_argument|
|
153
|
+
Builder.new(components: parsing_argument.components).node
|
154
|
+
},
|
155
|
+
component.name
|
156
|
+
)
|
157
|
+
when Parsing::DotWord
|
158
|
+
AST::Function.new(
|
159
|
+
[node_of_component(component.target)],
|
160
|
+
component.name
|
161
|
+
)
|
162
|
+
when Parsing::DotOperator
|
163
|
+
AST::Function.new(
|
164
|
+
[node_of_component(component.target)] + component.arguments.map {|parsing_argument|
|
165
|
+
Builder.new(components: parsing_argument.components).node
|
166
|
+
},
|
167
|
+
component.name
|
168
|
+
)
|
169
|
+
else
|
170
|
+
raise Exceptions::ASTError.new("Unhandled component, #{component}")
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def consume_operators!
|
175
|
+
loop do
|
176
|
+
break if @priorities.empty?
|
177
|
+
max_priority = @priorities.max
|
178
|
+
break if max_priority < 0
|
179
|
+
|
180
|
+
consume_operators_with_priority!(max_priority)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def consume_operators_with_priority!(priority)
|
185
|
+
p_indexes = @priorities.map.with_index.select {|p,index| p == priority}
|
186
|
+
# :left, :right, or :none
|
187
|
+
associativity = AST::Operator.associativity_of_priority(priority)
|
188
|
+
|
189
|
+
if associativity == :right
|
190
|
+
index = p_indexes[-1][1]
|
191
|
+
else
|
192
|
+
index = p_indexes[0][1]
|
193
|
+
end
|
194
|
+
|
195
|
+
operator = @nodes[index]
|
196
|
+
|
197
|
+
# If has unary operators after, must process those first
|
198
|
+
if @nodes[index+1].is_a?(Keisan::Parsing::UnaryOperator)
|
199
|
+
loop do
|
200
|
+
break if !@nodes[index+1].is_a?(Keisan::Parsing::UnaryOperator)
|
201
|
+
index += 1
|
202
|
+
end
|
203
|
+
operator = @nodes[index]
|
204
|
+
end
|
205
|
+
|
206
|
+
# operator is the current operator to process, and index is its index
|
207
|
+
if operator.is_a?(Keisan::Parsing::UnaryOperator)
|
208
|
+
replacement_node = operator.node_class.new(
|
209
|
+
children = [@nodes[index+1]]
|
210
|
+
)
|
211
|
+
@nodes.delete_if.with_index {|node, i| i >= index && i <= index+1}
|
212
|
+
@priorities.delete_if.with_index {|node, i| i >= index && i <= index+1}
|
213
|
+
@nodes.insert(index, replacement_node)
|
214
|
+
@priorities.insert(index, -1)
|
215
|
+
elsif operator.is_a?(Keisan::Parsing::Operator)
|
216
|
+
replacement_node = operator.node_class.new(
|
217
|
+
children = [@nodes[index-1],@nodes[index+1]],
|
218
|
+
parsing_operators = [operator]
|
219
|
+
)
|
220
|
+
@nodes.delete_if.with_index {|node, i| i >= index-1 && i <= index+1}
|
221
|
+
@priorities.delete_if.with_index {|node, i| i >= index-1 && i <= index+1}
|
222
|
+
@nodes.insert(index-1, replacement_node)
|
223
|
+
@priorities.insert(index-1, -1)
|
224
|
+
else
|
225
|
+
raise Keisan::Exceptions::ASTError.new("Can only consume operators")
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|