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
@@ -1,15 +1,15 @@
|
|
1
1
|
module Keisan
|
2
2
|
module Functions
|
3
|
-
class Filter <
|
3
|
+
class Filter < Function
|
4
4
|
# Filters (list, variable, expression)
|
5
5
|
# e.g. filter([1,2,3,4], x, x % 2 == 0)
|
6
6
|
# should give [2,4]
|
7
7
|
def initialize
|
8
|
-
|
8
|
+
super("filter", 3)
|
9
9
|
end
|
10
10
|
|
11
11
|
def value(ast_function, context = nil)
|
12
|
-
evaluate(ast_function, context
|
12
|
+
evaluate(ast_function, context)
|
13
13
|
end
|
14
14
|
|
15
15
|
def evaluate(ast_function, context = nil)
|
@@ -18,21 +18,23 @@ module Keisan
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def simplify(ast_function, context = nil)
|
21
|
+
validate_arguments!(ast_function.children.count)
|
22
|
+
|
23
|
+
context ||= Context.new
|
21
24
|
list, variable, expression = list_variable_expression_for(ast_function, context)
|
22
25
|
|
23
|
-
context ||= Keisan::Context.new
|
24
26
|
local = context.spawn_child(transient: false, shadowed: [variable.name])
|
25
27
|
|
26
|
-
|
28
|
+
AST::List.new(
|
27
29
|
list.children.select do |element|
|
28
30
|
local.register_variable!(variable, element)
|
29
31
|
result = expression.evaluate(local)
|
30
32
|
|
31
33
|
case result
|
32
|
-
when
|
34
|
+
when AST::Boolean
|
33
35
|
result.value
|
34
36
|
else
|
35
|
-
raise
|
37
|
+
raise Exceptions::InvalidFunctionError.new("Filter requires expression to be a logical expression")
|
36
38
|
end
|
37
39
|
end
|
38
40
|
)
|
@@ -41,20 +43,16 @@ module Keisan
|
|
41
43
|
private
|
42
44
|
|
43
45
|
def list_variable_expression_for(ast_function, context)
|
44
|
-
unless ast_function.children.size == 3
|
45
|
-
raise Keisan::Exceptions::InvalidFunctionError.new("Require 3 arguments to filter")
|
46
|
-
end
|
47
|
-
|
48
46
|
list = ast_function.children[0].simplify(context)
|
49
47
|
variable = ast_function.children[1]
|
50
48
|
expression = ast_function.children[2]
|
51
49
|
|
52
|
-
unless list.is_a?(
|
53
|
-
raise
|
50
|
+
unless list.is_a?(AST::List)
|
51
|
+
raise Exceptions::InvalidFunctionError.new("First argument to filter must be a list")
|
54
52
|
end
|
55
53
|
|
56
|
-
unless variable.is_a?(
|
57
|
-
raise
|
54
|
+
unless variable.is_a?(AST::Variable)
|
55
|
+
raise Exceptions::InvalidFunctionError.new("Second argument to filter must be a variable")
|
58
56
|
end
|
59
57
|
|
60
58
|
[list, variable, expression]
|
data/lib/keisan/functions/if.rb
CHANGED
@@ -1,32 +1,22 @@
|
|
1
1
|
module Keisan
|
2
2
|
module Functions
|
3
|
-
class If <
|
3
|
+
class If < Function
|
4
4
|
def initialize
|
5
|
-
|
5
|
+
super("if", ::Range.new(2,3))
|
6
6
|
end
|
7
7
|
|
8
8
|
def value(ast_function, context = nil)
|
9
|
-
|
10
|
-
|
11
|
-
unless (2..3).cover? ast_function.children.size
|
12
|
-
raise Keisan::Exceptions::InvalidFunctionError.new("Require 2 or 3 arguments to if")
|
13
|
-
end
|
14
|
-
|
15
|
-
bool = ast_function.children[0].value(context)
|
16
|
-
|
17
|
-
if bool
|
18
|
-
ast_function.children[1].value(context)
|
19
|
-
else
|
20
|
-
ast_function.children.size == 3 ? ast_function.children[2].value(context) : nil
|
21
|
-
end
|
9
|
+
validate_arguments!(ast_function.children.count)
|
10
|
+
evaluate(ast_function, context)
|
22
11
|
end
|
23
12
|
|
24
13
|
def evaluate(ast_function, context = nil)
|
25
|
-
|
14
|
+
validate_arguments!(ast_function.children.count)
|
15
|
+
context ||= Context.new
|
26
16
|
|
27
17
|
bool = ast_function.children[0].evaluate(context)
|
28
18
|
|
29
|
-
if bool.is_a?(
|
19
|
+
if bool.is_a?(AST::Boolean)
|
30
20
|
node = bool.value ? ast_function.children[1] : ast_function.children[2]
|
31
21
|
node.to_node.evaluate(context)
|
32
22
|
else
|
@@ -35,14 +25,17 @@ module Keisan
|
|
35
25
|
end
|
36
26
|
|
37
27
|
def simplify(ast_function, context = nil)
|
28
|
+
validate_arguments!(ast_function.children.count)
|
38
29
|
context ||= Context.new
|
39
30
|
bool = ast_function.children[0].simplify(context)
|
40
31
|
|
41
|
-
if bool.is_a?(
|
32
|
+
if bool.is_a?(AST::Boolean)
|
42
33
|
if bool.value
|
43
34
|
ast_function.children[1].to_node.simplify(context)
|
44
|
-
|
35
|
+
elsif ast_function.children.size >= 2
|
45
36
|
ast_function.children[2].to_node.simplify(context)
|
37
|
+
else
|
38
|
+
Keisan::AST::Null.new
|
46
39
|
end
|
47
40
|
else
|
48
41
|
ast_function
|
@@ -50,8 +43,9 @@ module Keisan
|
|
50
43
|
end
|
51
44
|
|
52
45
|
def differentiate(ast_function, variable, context = nil)
|
46
|
+
validate_arguments!(ast_function.children.count)
|
53
47
|
context ||= Context.new
|
54
|
-
|
48
|
+
AST::Function.new(
|
55
49
|
[
|
56
50
|
ast_function.children[0],
|
57
51
|
ast_function.children[1].differentiate(variable, context),
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Keisan
|
2
|
+
module Functions
|
3
|
+
class Let < Function
|
4
|
+
def initialize
|
5
|
+
super("let", ::Range.new(1,2))
|
6
|
+
end
|
7
|
+
|
8
|
+
def value(ast_function, context = nil)
|
9
|
+
evaluate(ast_function, context)
|
10
|
+
end
|
11
|
+
|
12
|
+
def evaluate(ast_function, context = nil)
|
13
|
+
validate_arguments!(ast_function.children.count)
|
14
|
+
assignment(ast_function).evaluate(context)
|
15
|
+
end
|
16
|
+
|
17
|
+
def simplify(ast_function, context = nil)
|
18
|
+
evaluate(ast_function, context)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def assignment(ast_function)
|
24
|
+
if ast_function.children.count == 1
|
25
|
+
unless ast_function.children.first.is_a?(AST::Assignment)
|
26
|
+
raise Exceptions::InvalidFunctionError.new("`let` must accept assignment if given one argument")
|
27
|
+
end
|
28
|
+
|
29
|
+
AST::Assignment.new(ast_function.children.first.children, local: true)
|
30
|
+
else
|
31
|
+
AST::Assignment.new(ast_function.children, local: true)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/keisan/functions/map.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
module Keisan
|
2
2
|
module Functions
|
3
|
-
class Map <
|
3
|
+
class Map < Function
|
4
4
|
# Maps (list, variable, expression)
|
5
5
|
# e.g. map([1,2,3], x, 2*x)
|
6
6
|
# should give [2,4,6]
|
7
7
|
def initialize
|
8
|
-
|
8
|
+
super("map", 3)
|
9
9
|
end
|
10
10
|
|
11
11
|
def value(ast_function, context = nil)
|
12
|
-
evaluate(ast_function, context
|
12
|
+
evaluate(ast_function, context)
|
13
13
|
end
|
14
14
|
|
15
15
|
def evaluate(ast_function, context = nil)
|
@@ -18,12 +18,14 @@ module Keisan
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def simplify(ast_function, context = nil)
|
21
|
-
|
21
|
+
validate_arguments!(ast_function.children.count)
|
22
|
+
|
23
|
+
context ||= Context.new
|
22
24
|
list, variable, expression = list_variable_expression_for(ast_function, context)
|
23
25
|
|
24
26
|
local = context.spawn_child(transient: false, shadowed: [variable.name])
|
25
27
|
|
26
|
-
|
28
|
+
AST::List.new(
|
27
29
|
list.children.map do |element|
|
28
30
|
local.register_variable!(variable, element)
|
29
31
|
expression.simplified(local)
|
@@ -34,20 +36,16 @@ module Keisan
|
|
34
36
|
private
|
35
37
|
|
36
38
|
def list_variable_expression_for(ast_function, context)
|
37
|
-
unless ast_function.children.size == 3
|
38
|
-
raise Keisan::Exceptions::InvalidFunctionError.new("Require 3 arguments to map")
|
39
|
-
end
|
40
|
-
|
41
39
|
list = ast_function.children[0].simplify(context)
|
42
40
|
variable = ast_function.children[1]
|
43
41
|
expression = ast_function.children[2]
|
44
42
|
|
45
|
-
unless list.is_a?(
|
46
|
-
raise
|
43
|
+
unless list.is_a?(AST::List)
|
44
|
+
raise Exceptions::InvalidFunctionError.new("First argument to map must be a list")
|
47
45
|
end
|
48
46
|
|
49
|
-
unless variable.is_a?(
|
50
|
-
raise
|
47
|
+
unless variable.is_a?(AST::Variable)
|
48
|
+
raise Exceptions::InvalidFunctionError.new("Second argument to map must be a variable")
|
51
49
|
end
|
52
50
|
|
53
51
|
[list, variable, expression]
|
@@ -11,7 +11,7 @@ module Keisan
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def differentiate(ast_function, variable, context = nil)
|
14
|
-
raise
|
14
|
+
raise Exceptions::InvalidFunctionError.new unless ast_function.children.count == 1
|
15
15
|
context ||= Context.new
|
16
16
|
|
17
17
|
argument_simplified = ast_function.children.first.simplify(context)
|
@@ -23,7 +23,7 @@ module Keisan
|
|
23
23
|
protected
|
24
24
|
|
25
25
|
def self.derivative(argument)
|
26
|
-
raise
|
26
|
+
raise Exceptions::NotImplementedError.new
|
27
27
|
end
|
28
28
|
|
29
29
|
def self.apply_simplifications(simplified)
|
@@ -1,27 +1,30 @@
|
|
1
1
|
module Keisan
|
2
2
|
module Functions
|
3
|
-
class ProcFunction <
|
3
|
+
class ProcFunction < Function
|
4
4
|
attr_reader :function_proc
|
5
5
|
|
6
6
|
def initialize(name, function_proc)
|
7
|
-
raise
|
7
|
+
raise Exceptions::InvalidFunctionError.new unless function_proc.is_a?(Proc)
|
8
8
|
|
9
|
-
super(name)
|
9
|
+
super(name, function_proc.arity)
|
10
10
|
@function_proc = function_proc
|
11
11
|
end
|
12
12
|
|
13
13
|
def call(context, *args)
|
14
|
+
validate_arguments!(args.count)
|
14
15
|
function_proc.call(*args).to_node
|
15
16
|
end
|
16
17
|
|
17
18
|
def value(ast_function, context = nil)
|
18
|
-
|
19
|
+
validate_arguments!(ast_function.children.count)
|
20
|
+
context ||= Context.new
|
19
21
|
argument_values = ast_function.children.map {|child| child.value(context)}
|
20
22
|
call(context, *argument_values).value(context)
|
21
23
|
end
|
22
24
|
|
23
25
|
def evaluate(ast_function, context = nil)
|
24
|
-
|
26
|
+
validate_arguments!(ast_function.children.count)
|
27
|
+
context ||= Context.new
|
25
28
|
|
26
29
|
ast_function.instance_variable_set(
|
27
30
|
:@children,
|
@@ -36,6 +39,7 @@ module Keisan
|
|
36
39
|
end
|
37
40
|
|
38
41
|
def simplify(ast_function, context = nil)
|
42
|
+
validate_arguments!(ast_function.children.count)
|
39
43
|
context ||= Context.new
|
40
44
|
|
41
45
|
ast_function.instance_variable_set(
|
@@ -43,7 +47,7 @@ module Keisan
|
|
43
47
|
ast_function.children.map {|child| child.evaluate(context)}
|
44
48
|
)
|
45
49
|
|
46
|
-
if ast_function.children.all? {|child| child.is_a?(
|
50
|
+
if ast_function.children.all? {|child| child.is_a?(AST::ConstantLiteral)}
|
47
51
|
value(ast_function, context).to_node.simplify(context)
|
48
52
|
else
|
49
53
|
ast_function
|
@@ -3,6 +3,7 @@ module Keisan
|
|
3
3
|
class Rand < ProcFunction
|
4
4
|
def initialize
|
5
5
|
@name = "rand"
|
6
|
+
@arity = ::Range.new(1,2)
|
6
7
|
end
|
7
8
|
|
8
9
|
# Single argument: integer in range [0, max)
|
@@ -14,7 +15,7 @@ module Keisan
|
|
14
15
|
when 2
|
15
16
|
context.random.rand(args.first...args.last)
|
16
17
|
else
|
17
|
-
raise
|
18
|
+
raise Exceptions::InvalidFunctionError.new
|
18
19
|
end
|
19
20
|
end
|
20
21
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Keisan
|
2
|
+
module Functions
|
3
|
+
class Range < Function
|
4
|
+
def initialize
|
5
|
+
super("range", ::Range.new(1,3))
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(context, *args)
|
9
|
+
start, finish, shift = start_finish_shift_from_args(*args)
|
10
|
+
|
11
|
+
if shift == 1
|
12
|
+
start_finish_range(start, finish)
|
13
|
+
else
|
14
|
+
start_finish_shift_range(start, finish, shift)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def value(ast_function, context = nil)
|
19
|
+
validate_arguments!(ast_function.children.count)
|
20
|
+
evaluate(ast_function, context)
|
21
|
+
end
|
22
|
+
|
23
|
+
def evaluate(ast_function, context = nil)
|
24
|
+
validate_arguments!(ast_function.children.count)
|
25
|
+
context ||= Keisan::Context.new
|
26
|
+
simplify(ast_function, context)
|
27
|
+
end
|
28
|
+
|
29
|
+
def simplify(ast_function, context = nil)
|
30
|
+
validate_arguments!(ast_function.children.count)
|
31
|
+
context ||= Context.new
|
32
|
+
|
33
|
+
simplified_children = ast_function.children.map {|child| child.simplify(context)}
|
34
|
+
|
35
|
+
if simplified_children.all? {|child| child.is_a?(Keisan::AST::Number)}
|
36
|
+
Keisan::AST::List.new(call(context, *simplified_children.map(&:value)))
|
37
|
+
else
|
38
|
+
Keisan::AST::Function.new(simplified_children, "range")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def start_finish_shift_from_args(*args)
|
45
|
+
case args.count
|
46
|
+
when 1
|
47
|
+
[0, args[0], 1]
|
48
|
+
when 2
|
49
|
+
[args[0], args[1], 1]
|
50
|
+
when 3
|
51
|
+
[args[0], args[1], args[2]]
|
52
|
+
else
|
53
|
+
[0, 0, 0]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def start_finish_range(start, finish)
|
58
|
+
(start...finish).to_a
|
59
|
+
end
|
60
|
+
|
61
|
+
def start_finish_shift_range(start, finish, shift)
|
62
|
+
if shift == 0 or !shift.is_a?(Integer)
|
63
|
+
raise Keisan::Exceptions::InvalidFunctionError.new("shift argument for Range must be non-zero integer")
|
64
|
+
end
|
65
|
+
|
66
|
+
if shift > 0
|
67
|
+
(start...finish).select {|i| (i - start) % shift == 0}
|
68
|
+
else
|
69
|
+
(finish+1...start+1).select {|i| (i - finish) % shift == 0}.reverse
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -1,15 +1,15 @@
|
|
1
1
|
module Keisan
|
2
2
|
module Functions
|
3
|
-
class Reduce <
|
3
|
+
class Reduce < Function
|
4
4
|
# Reduces (list, initial, accumulator, variable, expression)
|
5
5
|
# e.g. reduce([1,2,3,4], 0, total, x, total+x)
|
6
6
|
# should give 10
|
7
7
|
def initialize
|
8
|
-
|
8
|
+
super("reduce", 5)
|
9
9
|
end
|
10
10
|
|
11
11
|
def value(ast_function, context = nil)
|
12
|
-
evaluate(ast_function, context
|
12
|
+
evaluate(ast_function, context)
|
13
13
|
end
|
14
14
|
|
15
15
|
def evaluate(ast_function, context = nil)
|
@@ -18,7 +18,9 @@ module Keisan
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def simplify(ast_function, context = nil)
|
21
|
-
|
21
|
+
validate_arguments!(ast_function.children.count)
|
22
|
+
|
23
|
+
context ||= Context.new
|
22
24
|
list, initial, accumulator, variable, expression = list_initial_accumulator_variable_expression_for(ast_function, context)
|
23
25
|
|
24
26
|
local = context.spawn_child(transient: false, shadowed: [accumulator.name, variable.name])
|
@@ -36,26 +38,22 @@ module Keisan
|
|
36
38
|
private
|
37
39
|
|
38
40
|
def list_initial_accumulator_variable_expression_for(ast_function, context)
|
39
|
-
unless ast_function.children.size == 5
|
40
|
-
raise Keisan::Exceptions::InvalidFunctionError.new("Require 5 arguments to reduce")
|
41
|
-
end
|
42
|
-
|
43
41
|
list = ast_function.children[0].simplify(context)
|
44
42
|
initial = ast_function.children[1]
|
45
43
|
accumulator = ast_function.children[2]
|
46
44
|
variable = ast_function.children[3]
|
47
45
|
expression = ast_function.children[4]
|
48
46
|
|
49
|
-
unless list.is_a?(
|
50
|
-
raise
|
47
|
+
unless list.is_a?(AST::List)
|
48
|
+
raise Exceptions::InvalidFunctionError.new("First argument to reduce must be a list")
|
51
49
|
end
|
52
50
|
|
53
|
-
unless accumulator.is_a?(
|
54
|
-
raise
|
51
|
+
unless accumulator.is_a?(AST::Variable)
|
52
|
+
raise Exceptions::InvalidFunctionError.new("Third argument to reduce is accumulator and must be a variable")
|
55
53
|
end
|
56
54
|
|
57
|
-
unless variable.is_a?(
|
58
|
-
raise
|
55
|
+
unless variable.is_a?(AST::Variable)
|
56
|
+
raise Exceptions::InvalidFunctionError.new("Fourth argument to reduce is variable and must be a variable")
|
59
57
|
end
|
60
58
|
|
61
59
|
[list, initial, accumulator, variable, expression]
|