Skip to content

Commit

Permalink
evaluate for enumerable function fully evaluates
Browse files Browse the repository at this point in the history
  • Loading branch information
project-eutopia committed Nov 23, 2018
1 parent 7d09195 commit a3fa107
Show file tree
Hide file tree
Showing 7 changed files with 23 additions and 21 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ calculator.evaluate("f(0-a)", a: 2)
#=> -20
calculator.evaluate("n") # n only exists in the definition of f(x)
#=> Keisan::Exceptions::UndefinedVariableError: n
calculator.evaluate("includes(a, element) = a.reduce(false, found, x, found || (x == element))")
calculator.evaluate("[3, 9].map(x, [1, 3, 5].includes(x))").value
#=> [true, false]
```

This form even supports recursion, but you must explicitly allow it.
Expand Down
12 changes: 10 additions & 2 deletions lib/keisan/functions/enumerable_function.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ 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
Expand All @@ -20,9 +24,9 @@ def evaluate(ast_function, context = nil)

case operand
when AST::List
evaluate_list(operand, arguments, expression, context)
evaluate_list(operand, arguments, expression, context).evaluate(context)
when AST::Hash
evaluate_hash(operand, arguments, expression, context)
evaluate_hash(operand, arguments, expression, context).evaluate(context)
else
raise Exceptions::InvalidFunctionError.new("Unhandled first argument to #{name}: #{operand}")
end
Expand All @@ -34,6 +38,10 @@ def simplify(ast_function, context = nil)

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")
Expand Down
8 changes: 2 additions & 6 deletions lib/keisan/functions/filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,8 @@ def initialize
super("filter")
end

def unbound_variables(children, context)
if children.size == 3
super - Set[children[1].name]
else
super - Set[children[1].name, children[2].name]
end
def shadowing_variable_names(children)
children.size == 3 ? children[1..1] : children[1..2]
end

private
Expand Down
8 changes: 2 additions & 6 deletions lib/keisan/functions/map.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,8 @@ def initialize
super("map")
end

def unbound_variables(children, context)
if children.size == 3
super - Set[children[1].name]
else
super - Set[children[1].name, children[2].name]
end
def shadowing_variable_names(children)
children.size == 3 ? children[1..1] : children[1..2]
end

private
Expand Down
8 changes: 2 additions & 6 deletions lib/keisan/functions/reduce.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,8 @@ def initialize
super("reduce")
end

def unbound_variables(children, context)
if children.size == 5
super - Set[children[2].name, children[3].name]
else
super - Set[children[2].name, children[3].name, children[4].name]
end
def shadowing_variable_names(children)
children.size == 5 ? children[2..3] : children[2..4]
end

protected
Expand Down
3 changes: 3 additions & 0 deletions spec/keisan/ast/function_assignment_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,8 @@
calculator = Keisan::Calculator.new
calculator.evaluate("minimum(a) = a.reduce(INF, current_min, element, if (element < current_min, element, current_min))")
expect(calculator.evaluate("minimum([5,1,3])")).to eq 1

calculator.evaluate("includes(a, element) = a.reduce(false, found, x, found || (x == element))")
expect(calculator.evaluate("[3, 9].map(x, [1, 3, 5].includes(x))")).to eq([true, false])
end
end
2 changes: 1 addition & 1 deletion spec/readme_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
digest = Digest::SHA256.hexdigest(content)

# cat README.md | sha256sum
expected_digest = "399be39e65ec8aaace1f9ce266415bf49627572bcd2ee6f8e05d9f4a62cc241c"
expected_digest = "c430ca4bd926e553e7414c96f89b8f81ef8bf3c491c5484e9f9599f38e06e2f7"
if digest != expected_digest
raise "Invalid README file detected with SHA256 digest of #{digest}. Use command `cat README.md | sha256sum` to get correct digest if your changes to the README are safe. Aborting README test."
end
Expand Down

0 comments on commit a3fa107

Please sign in to comment.