class Dentaku::Calculator

Attributes

aliases[R]
ast_cache[R]
case_sensitive[R]
memory[R]
nested_data_support[R]
raw_date_literals[R]
result[R]
tokenizer[R]

Public Class Methods

add_function(name, type, body, callback = nil) click to toggle source
# File lib/dentaku/calculator.rb, line 29
def self.add_function(name, type, body, callback = nil)
  Dentaku::AST::FunctionRegistry.default.register(name, type, body, callback)
end
add_functions(functions) click to toggle source
# File lib/dentaku/calculator.rb, line 33
def self.add_functions(functions)
  functions.each { |(name, type, body, callback)| add_function(name, type, body, callback) }
end
new(options = {}) click to toggle source
# File lib/dentaku/calculator.rb, line 15
def initialize(options = {})
  clear
  @tokenizer = Tokenizer.new
  @case_sensitive = options.delete(:case_sensitive)
  @aliases = options.delete(:aliases) || Dentaku.aliases
  @nested_data_support = options.fetch(:nested_data_support, true)
  options.delete(:nested_data_support)
  @raw_date_literals = options.fetch(:raw_date_literals, true)
  options.delete(:raw_date_literals)
  @ast_cache = options
  @disable_ast_cache = false
  @function_registry = Dentaku::AST::FunctionRegistry.new
end

Public Instance Methods

add_function(name, type, body, callback = nil) click to toggle source
# File lib/dentaku/calculator.rb, line 37
def add_function(name, type, body, callback = nil)
  @function_registry.register(name, type, body, callback)
  self
end
add_functions(functions) click to toggle source
# File lib/dentaku/calculator.rb, line 42
def add_functions(functions)
  functions.each { |(name, type, body, callback)| add_function(name, type, body, callback) }
  self
end
ast(expression) click to toggle source
# File lib/dentaku/calculator.rb, line 109
def ast(expression)
  return expression if expression.is_a?(AST::Node)
  return expression.map { |e| ast(e) } if expression.is_a? Array

  @ast_cache.fetch(expression) {
    options = {
      aliases: aliases,
      case_sensitive: case_sensitive,
      function_registry: @function_registry,
      raw_date_literals: raw_date_literals
    }

    tokens = tokenizer.tokenize(expression, options)
    Parser.new(tokens, options).parse.tap do |node|
      @ast_cache[expression] = node if cache_ast?
    end
  }
end
bind(key_or_hash, value = nil)
Alias for: store
cache_ast?() click to toggle source
# File lib/dentaku/calculator.rb, line 188
def cache_ast?
  Dentaku.cache_ast? && !@disable_ast_cache
end
clear() click to toggle source
# File lib/dentaku/calculator.rb, line 180
def clear
  @memory = {}
end
clear_cache(pattern = :all) click to toggle source
# File lib/dentaku/calculator.rb, line 132
def clear_cache(pattern = :all)
  case pattern
  when :all
    @ast_cache = {}
  when String
    @ast_cache.delete(pattern)
  when Regexp
    @ast_cache.delete_if { |k, _| k =~ pattern }
  else
    raise ::ArgumentError
  end
end
dependencies(expression, context = {}) click to toggle source
# File lib/dentaku/calculator.rb, line 96
def dependencies(expression, context = {})
  test_context = context.nil? ? {} : store(context) { memory }

  case expression
  when Dentaku::AST::Node
    expression.dependencies(test_context)
  when Array
    expression.flat_map { |e| dependencies(e, context) }
  else
    ast(expression).dependencies(test_context)
  end
end
disable_cache() { |self| ... } click to toggle source
# File lib/dentaku/calculator.rb, line 47
def disable_cache
  @disable_ast_cache = true
  yield(self) if block_given?
ensure
  @disable_ast_cache = false
end
empty?() click to toggle source
# File lib/dentaku/calculator.rb, line 184
def empty?
  memory.empty?
end
evaluate(expression, data = {}, &block) click to toggle source
# File lib/dentaku/calculator.rb, line 54
def evaluate(expression, data = {}, &block)
  context = evaluation_context(data, :permissive)
  return evaluate_array(expression, context, &block) if expression.is_a?(Array)

  evaluate!(expression, context)
rescue Dentaku::Error, Dentaku::ArgumentError, Dentaku::ZeroDivisionError => ex
  block.call(expression, ex) if block_given?
end
evaluate!(expression, data = {}, &block) click to toggle source
# File lib/dentaku/calculator.rb, line 67
def evaluate!(expression, data = {}, &block)
  context = evaluation_context(data, :strict)
  return evaluate_array!(expression, context, &block) if expression.is_a? Array

  store(context) do
    node = ast(expression)
    unbound = node.dependencies(memory)

    unless unbound.empty?
      raise UnboundVariableError.new(unbound),
            "no value provided for variables: #{unbound.uniq.join(', ')}"
    end

    node.value(memory)
  end
end
evaluation_context(data, evaluation_mode) click to toggle source
# File lib/dentaku/calculator.rb, line 145
def evaluation_context(data, evaluation_mode)
  data.key?(:__evaluation_mode) ? data : data.merge(__evaluation_mode: evaluation_mode)
end
load_cache(ast_cache) click to toggle source
# File lib/dentaku/calculator.rb, line 128
def load_cache(ast_cache)
  @ast_cache = ast_cache
end
solve(expression_hash, &block) click to toggle source
# File lib/dentaku/calculator.rb, line 92
def solve(expression_hash, &block)
  BulkExpressionSolver.new(expression_hash, self).solve(&block)
end
solve!(expression_hash) click to toggle source
# File lib/dentaku/calculator.rb, line 88
def solve!(expression_hash)
  BulkExpressionSolver.new(expression_hash, self).solve!
end
store(key_or_hash, value = nil) { || ... } click to toggle source
# File lib/dentaku/calculator.rb, line 149
def store(key_or_hash, value = nil)
  restore = Hash[memory]

  if value.nil?
    key_or_hash = FlatHash.from_hash_with_intermediates(key_or_hash) if nested_data_support
    key_or_hash.each do |key, val|
      memory[standardize_case(key.to_s)] = val
    end
  else
    memory[standardize_case(key_or_hash.to_s)] = value
  end

  if block_given?
    begin
      result = yield
      @memory = restore
      return result
    rescue => e
      @memory = restore
      raise e
    end
  end

  self
end
Also aliased as: bind
store_formula(key, formula) click to toggle source
# File lib/dentaku/calculator.rb, line 176
def store_formula(key, formula)
  store(key, ast(formula))
end

Private Instance Methods

evaluate_array(expression, data = {}, &block) click to toggle source
# File lib/dentaku/calculator.rb, line 63
        def evaluate_array(expression, data = {}, &block)
  expression.map { |e| evaluate(e, data, &block) }
end
evaluate_array!(expression, data = {}, &block) click to toggle source
# File lib/dentaku/calculator.rb, line 84
        def evaluate_array!(expression, data = {}, &block)
  expression.map { |e| evaluate!(e, data, &block) }
end