-
Notifications
You must be signed in to change notification settings - Fork 10
/
enumerable_function.rb
67 lines (53 loc) · 1.99 KB
/
enumerable_function.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
module Keisan
module Functions
class EnumerableFunction < Function
# Filters lists/hashes:
# (list, variable, boolean_expression)
# (hash, key, value, boolean_expression)
def initialize(name)
super(name, -3)
end
def value(ast_function, context = nil)
evaluate(ast_function, context)
end
def unbound_variables(children, context)
super - Set.new(shadowing_variable_names(children).map(&:name))
end
def evaluate(ast_function, context = nil)
validate_arguments!(ast_function.children.count)
context ||= Context.new
operand, arguments, expression = operand_arguments_expression_for(ast_function, context)
# Extract underlying operand for cells
real_operand = operand.is_a?(AST::Cell) ? operand.node : operand
case real_operand
when AST::List
evaluate_list(real_operand, arguments, expression, context).evaluate(context)
when AST::Hash
evaluate_hash(real_operand, arguments, expression, context).evaluate(context)
else
raise Exceptions::InvalidFunctionError.new("Unhandled first argument to #{name}: #{real_operand}")
end
end
def simplify(ast_function, context = nil)
evaluate(ast_function, context)
end
protected
def shadowing_variable_names(children)
raise Exceptions::NotImplementedError.new
end
def verify_arguments!(arguments)
unless arguments.all? {|argument| argument.is_a?(AST::Variable)}
raise Exceptions::InvalidFunctionError.new("Middle arguments to #{name} must be variables")
end
end
private
def operand_arguments_expression_for(ast_function, context)
operand = ast_function.children[0].simplify(context)
arguments = ast_function.children[1...-1]
expression = ast_function.children[-1]
verify_arguments!(arguments)
[operand, arguments, expression]
end
end
end
end