keisan 0.1.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 +7 -0
 - data/.gitignore +12 -0
 - data/.rspec +1 -0
 - data/.travis.yml +6 -0
 - data/Gemfile +4 -0
 - data/MIT-LICENSE +19 -0
 - data/README.md +188 -0
 - data/Rakefile +6 -0
 - data/bin/console +10 -0
 - data/bin/setup +8 -0
 - data/keisan.gemspec +31 -0
 - data/lib/keisan.rb +118 -0
 - data/lib/keisan/ast/arithmetic_operator.rb +9 -0
 - data/lib/keisan/ast/bitwise_and.rb +21 -0
 - data/lib/keisan/ast/bitwise_operator.rb +9 -0
 - data/lib/keisan/ast/bitwise_or.rb +21 -0
 - data/lib/keisan/ast/bitwise_xor.rb +21 -0
 - data/lib/keisan/ast/boolean.rb +15 -0
 - data/lib/keisan/ast/builder.rb +141 -0
 - data/lib/keisan/ast/exponent.rb +25 -0
 - data/lib/keisan/ast/function.rb +19 -0
 - data/lib/keisan/ast/indexing.rb +16 -0
 - data/lib/keisan/ast/list.rb +10 -0
 - data/lib/keisan/ast/literal.rb +6 -0
 - data/lib/keisan/ast/logical_and.rb +21 -0
 - data/lib/keisan/ast/logical_greater_than.rb +17 -0
 - data/lib/keisan/ast/logical_greater_than_or_equal_to.rb +17 -0
 - data/lib/keisan/ast/logical_less_than.rb +17 -0
 - data/lib/keisan/ast/logical_less_than_or_equal_to.rb +17 -0
 - data/lib/keisan/ast/logical_operator.rb +9 -0
 - data/lib/keisan/ast/logical_or.rb +21 -0
 - data/lib/keisan/ast/node.rb +9 -0
 - data/lib/keisan/ast/null.rb +12 -0
 - data/lib/keisan/ast/number.rb +15 -0
 - data/lib/keisan/ast/operator.rb +50 -0
 - data/lib/keisan/ast/parent.rb +15 -0
 - data/lib/keisan/ast/plus.rb +49 -0
 - data/lib/keisan/ast/string.rb +15 -0
 - data/lib/keisan/ast/times.rb +36 -0
 - data/lib/keisan/ast/unary_bitwise_not.rb +9 -0
 - data/lib/keisan/ast/unary_identity.rb +9 -0
 - data/lib/keisan/ast/unary_inverse.rb +9 -0
 - data/lib/keisan/ast/unary_logical_not.rb +9 -0
 - data/lib/keisan/ast/unary_minus.rb +9 -0
 - data/lib/keisan/ast/unary_operator.rb +13 -0
 - data/lib/keisan/ast/unary_plus.rb +9 -0
 - data/lib/keisan/ast/variable.rb +16 -0
 - data/lib/keisan/calculator.rb +30 -0
 - data/lib/keisan/context.rb +36 -0
 - data/lib/keisan/exceptions.rb +19 -0
 - data/lib/keisan/function.rb +15 -0
 - data/lib/keisan/functions/default_registry.rb +58 -0
 - data/lib/keisan/functions/rand.rb +22 -0
 - data/lib/keisan/functions/registry.rb +50 -0
 - data/lib/keisan/functions/sample.rb +20 -0
 - data/lib/keisan/parser.rb +211 -0
 - data/lib/keisan/parsing/argument.rb +6 -0
 - data/lib/keisan/parsing/arithmetic_operator.rb +6 -0
 - data/lib/keisan/parsing/bitwise_and.rb +9 -0
 - data/lib/keisan/parsing/bitwise_not.rb +9 -0
 - data/lib/keisan/parsing/bitwise_not_not.rb +9 -0
 - data/lib/keisan/parsing/bitwise_operator.rb +6 -0
 - data/lib/keisan/parsing/bitwise_or.rb +9 -0
 - data/lib/keisan/parsing/bitwise_xor.rb +9 -0
 - data/lib/keisan/parsing/boolean.rb +11 -0
 - data/lib/keisan/parsing/component.rb +6 -0
 - data/lib/keisan/parsing/divide.rb +9 -0
 - data/lib/keisan/parsing/element.rb +6 -0
 - data/lib/keisan/parsing/exponent.rb +9 -0
 - data/lib/keisan/parsing/function.rb +12 -0
 - data/lib/keisan/parsing/group.rb +11 -0
 - data/lib/keisan/parsing/indexing.rb +14 -0
 - data/lib/keisan/parsing/list.rb +10 -0
 - data/lib/keisan/parsing/logical_and.rb +9 -0
 - data/lib/keisan/parsing/logical_greater_than.rb +9 -0
 - data/lib/keisan/parsing/logical_greater_than_or_equal_to.rb +9 -0
 - data/lib/keisan/parsing/logical_less_than.rb +9 -0
 - data/lib/keisan/parsing/logical_less_than_or_equal_to.rb +9 -0
 - data/lib/keisan/parsing/logical_not.rb +9 -0
 - data/lib/keisan/parsing/logical_not_not.rb +9 -0
 - data/lib/keisan/parsing/logical_operator.rb +6 -0
 - data/lib/keisan/parsing/logical_or.rb +9 -0
 - data/lib/keisan/parsing/minus.rb +9 -0
 - data/lib/keisan/parsing/null.rb +6 -0
 - data/lib/keisan/parsing/number.rb +10 -0
 - data/lib/keisan/parsing/operator.rb +13 -0
 - data/lib/keisan/parsing/plus.rb +9 -0
 - data/lib/keisan/parsing/round_group.rb +6 -0
 - data/lib/keisan/parsing/square_group.rb +6 -0
 - data/lib/keisan/parsing/string.rb +10 -0
 - data/lib/keisan/parsing/times.rb +9 -0
 - data/lib/keisan/parsing/unary_minus.rb +9 -0
 - data/lib/keisan/parsing/unary_operator.rb +9 -0
 - data/lib/keisan/parsing/unary_plus.rb +9 -0
 - data/lib/keisan/parsing/variable.rb +11 -0
 - data/lib/keisan/token.rb +25 -0
 - data/lib/keisan/tokenizer.rb +41 -0
 - data/lib/keisan/tokens/arithmetic_operator.rb +29 -0
 - data/lib/keisan/tokens/bitwise_operator.rb +29 -0
 - data/lib/keisan/tokens/boolean.rb +20 -0
 - data/lib/keisan/tokens/comma.rb +11 -0
 - data/lib/keisan/tokens/group.rb +28 -0
 - data/lib/keisan/tokens/logical_operator.rb +38 -0
 - data/lib/keisan/tokens/null.rb +15 -0
 - data/lib/keisan/tokens/number.rb +24 -0
 - data/lib/keisan/tokens/operator.rb +9 -0
 - data/lib/keisan/tokens/string.rb +15 -0
 - data/lib/keisan/tokens/word.rb +11 -0
 - data/lib/keisan/variables/default_registry.rb +20 -0
 - data/lib/keisan/variables/registry.rb +41 -0
 - data/lib/keisan/version.rb +3 -0
 - metadata +238 -0
 
| 
         @@ -0,0 +1,50 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Keisan
         
     | 
| 
      
 2 
     | 
    
         
            +
              module AST
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Operator < Parent
         
     | 
| 
      
 4 
     | 
    
         
            +
                  def initialize(children = [], parsing_operators = [])
         
     | 
| 
      
 5 
     | 
    
         
            +
                    unless children.count == parsing_operators.count + 1
         
     | 
| 
      
 6 
     | 
    
         
            +
                      raise Keisan::Exceptions::ASTError.new("Mismatch of children and operators")
         
     | 
| 
      
 7 
     | 
    
         
            +
                    end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                    unless arity.include?(children.count)
         
     | 
| 
      
 10 
     | 
    
         
            +
                      raise Keisan::Exceptions::ASTError.new("Invalid number of arguments")
         
     | 
| 
      
 11 
     | 
    
         
            +
                    end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                    children = Array.wrap(children)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    super(children)
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    @parsing_operators = parsing_operators
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  def arity
         
     | 
| 
      
 21 
     | 
    
         
            +
                    raise Keisan::Exceptions::NotImplementedError.new
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  def associativity
         
     | 
| 
      
 25 
     | 
    
         
            +
                    raise Keisan::Exceptions::NotImplementedError.new
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  def symbol
         
     | 
| 
      
 29 
     | 
    
         
            +
                    raise Keisan::Exceptions::NotImplementedError.new
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  def blank_value
         
     | 
| 
      
 33 
     | 
    
         
            +
                    raise Keisan::Exceptions::NotImplementedError.new
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  def value(context = nil)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    args = children
         
     | 
| 
      
 38 
     | 
    
         
            +
                    args = args.reverse if associativity == :right
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                    args.inject(blank_value) do |result, child|
         
     | 
| 
      
 41 
     | 
    
         
            +
                      if associativity == :left
         
     | 
| 
      
 42 
     | 
    
         
            +
                        result.send(symbol, child.value(context))
         
     | 
| 
      
 43 
     | 
    
         
            +
                      else
         
     | 
| 
      
 44 
     | 
    
         
            +
                        child.value(context).send(symbol, result)
         
     | 
| 
      
 45 
     | 
    
         
            +
                      end
         
     | 
| 
      
 46 
     | 
    
         
            +
                    end
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Keisan
         
     | 
| 
      
 2 
     | 
    
         
            +
              module AST
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Parent < Node
         
     | 
| 
      
 4 
     | 
    
         
            +
                  attr_reader :children
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize(children = [])
         
     | 
| 
      
 7 
     | 
    
         
            +
                    children = Array.wrap(children)
         
     | 
| 
      
 8 
     | 
    
         
            +
                    unless children.is_a?(Array) && children.all? {|children| children.is_a?(Node)}
         
     | 
| 
      
 9 
     | 
    
         
            +
                      raise Keisan::Exceptions::InternalError.new
         
     | 
| 
      
 10 
     | 
    
         
            +
                    end
         
     | 
| 
      
 11 
     | 
    
         
            +
                    @children = children
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,49 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Keisan
         
     | 
| 
      
 2 
     | 
    
         
            +
              module AST
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Plus < ArithmeticOperator
         
     | 
| 
      
 4 
     | 
    
         
            +
                  def initialize(children = [], parsing_operators = [])
         
     | 
| 
      
 5 
     | 
    
         
            +
                    super
         
     | 
| 
      
 6 
     | 
    
         
            +
                    convert_minus_to_plus!
         
     | 
| 
      
 7 
     | 
    
         
            +
                  end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  def self.priority
         
     | 
| 
      
 10 
     | 
    
         
            +
                    10
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  def arity
         
     | 
| 
      
 14 
     | 
    
         
            +
                    2..Float::INFINITY
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  def symbol
         
     | 
| 
      
 18 
     | 
    
         
            +
                    :+
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  def blank_value
         
     | 
| 
      
 22 
     | 
    
         
            +
                    0
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  def value(context = nil)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    children_values = children.map {|child| child.value(context)}
         
     | 
| 
      
 27 
     | 
    
         
            +
                    # Special case of string concatenation
         
     | 
| 
      
 28 
     | 
    
         
            +
                    if children_values.all? {|child| child.is_a?(::String)}
         
     | 
| 
      
 29 
     | 
    
         
            +
                      children_values.join
         
     | 
| 
      
 30 
     | 
    
         
            +
                    # Special case of array concatenation
         
     | 
| 
      
 31 
     | 
    
         
            +
                    elsif children_values.all? {|child| child.is_a?(::Array)}
         
     | 
| 
      
 32 
     | 
    
         
            +
                      children_values.inject([], &:+)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    else
         
     | 
| 
      
 34 
     | 
    
         
            +
                      children_values.inject(0, &:+)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    end
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  private
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  def convert_minus_to_plus!
         
     | 
| 
      
 41 
     | 
    
         
            +
                    @parsing_operators.each.with_index do |parsing_operator, index|
         
     | 
| 
      
 42 
     | 
    
         
            +
                      if parsing_operator.is_a?(Keisan::Parsing::Minus)
         
     | 
| 
      
 43 
     | 
    
         
            +
                        @children[index+1] = Keisan::AST::UnaryMinus.new(@children[index+1])
         
     | 
| 
      
 44 
     | 
    
         
            +
                      end
         
     | 
| 
      
 45 
     | 
    
         
            +
                    end
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
              end
         
     | 
| 
      
 49 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,36 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Keisan
         
     | 
| 
      
 2 
     | 
    
         
            +
              module AST
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Times < ArithmeticOperator
         
     | 
| 
      
 4 
     | 
    
         
            +
                  def initialize(children = [], parsing_operators = [])
         
     | 
| 
      
 5 
     | 
    
         
            +
                    super
         
     | 
| 
      
 6 
     | 
    
         
            +
                    convert_divide_to_inverse!
         
     | 
| 
      
 7 
     | 
    
         
            +
                  end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  def self.priority
         
     | 
| 
      
 10 
     | 
    
         
            +
                    20
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  def arity
         
     | 
| 
      
 14 
     | 
    
         
            +
                    2..Float::INFINITY
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  def symbol
         
     | 
| 
      
 18 
     | 
    
         
            +
                    :*
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  def blank_value
         
     | 
| 
      
 22 
     | 
    
         
            +
                    1
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  private
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  def convert_divide_to_inverse!
         
     | 
| 
      
 28 
     | 
    
         
            +
                    @parsing_operators.each.with_index do |parsing_operator, index|
         
     | 
| 
      
 29 
     | 
    
         
            +
                      if parsing_operator.is_a?(Keisan::Parsing::Divide)
         
     | 
| 
      
 30 
     | 
    
         
            +
                        @children[index+1] = Keisan::AST::UnaryInverse.new(@children[index+1])
         
     | 
| 
      
 31 
     | 
    
         
            +
                      end
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,13 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Keisan
         
     | 
| 
      
 2 
     | 
    
         
            +
              module AST
         
     | 
| 
      
 3 
     | 
    
         
            +
                class UnaryOperator < Parent
         
     | 
| 
      
 4 
     | 
    
         
            +
                  def initialize(children = [])
         
     | 
| 
      
 5 
     | 
    
         
            +
                    children = Array.wrap(children)
         
     | 
| 
      
 6 
     | 
    
         
            +
                    super
         
     | 
| 
      
 7 
     | 
    
         
            +
                    if children.count != 1
         
     | 
| 
      
 8 
     | 
    
         
            +
                      raise Keisan::Exceptions::ASTError.new("Unary operator takes has a single child")
         
     | 
| 
      
 9 
     | 
    
         
            +
                    end
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,16 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Keisan
         
     | 
| 
      
 2 
     | 
    
         
            +
              module AST
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Variable < Literal
         
     | 
| 
      
 4 
     | 
    
         
            +
                  attr_reader :name
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize(name)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    @name = name
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  def value(context = nil)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    context = Keisan::Context.new if context.nil?
         
     | 
| 
      
 12 
     | 
    
         
            +
                    context.variable(name)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,30 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Keisan
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Calculator
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :context
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize(context = nil)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @context = context || Context.new
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def evaluate(expression, definitions = {})
         
     | 
| 
      
 10 
     | 
    
         
            +
                  local_context = context.spawn_child
         
     | 
| 
      
 11 
     | 
    
         
            +
                  definitions.each do |name, value|
         
     | 
| 
      
 12 
     | 
    
         
            +
                    case value
         
     | 
| 
      
 13 
     | 
    
         
            +
                    when Proc
         
     | 
| 
      
 14 
     | 
    
         
            +
                      local_context.register_function!(name, value)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    else
         
     | 
| 
      
 16 
     | 
    
         
            +
                      local_context.register_variable!(name, value)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    end
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                  Keisan::AST::Builder.new(string: expression).ast.value(local_context)
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def define_variable!(name, value)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  context.register_variable!(name, value)
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def define_function!(name, function)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  context.register_function!(name, function)
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,36 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Keisan
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Context
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :function_registry, :variable_registry
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize(parent: nil, random: nil)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @parent = parent
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @function_registry = Functions::Registry.new(parent: @parent.try(:function_registry))
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @variable_registry = Variables::Registry.new(parent: @parent.try(:variable_registry))
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @random            = random
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def spawn_child
         
     | 
| 
      
 13 
     | 
    
         
            +
                  self.class.new(parent: self)
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def function(name)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @function_registry[name.to_s]
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def register_function!(name, function)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @function_registry.register!(name.to_s, function)
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def variable(name)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  @variable_registry[name.to_s]
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def register_variable!(name, value)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @variable_registry.register!(name.to_s, value)
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def random
         
     | 
| 
      
 33 
     | 
    
         
            +
                  @random || @parent.try(:random) || Random.new
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Keisan
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Exceptions
         
     | 
| 
      
 3 
     | 
    
         
            +
                class BaseError < ::StandardError; end
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                class InternalError < BaseError; end
         
     | 
| 
      
 6 
     | 
    
         
            +
                class StandardError < BaseError; end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                class NotImplementedError < InternalError; end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                class InvalidToken < StandardError; end
         
     | 
| 
      
 11 
     | 
    
         
            +
                class TokenizingError < StandardError; end
         
     | 
| 
      
 12 
     | 
    
         
            +
                class ParseError < StandardError; end
         
     | 
| 
      
 13 
     | 
    
         
            +
                class ASTError < StandardError; end
         
     | 
| 
      
 14 
     | 
    
         
            +
                class InvalidFunctionError < StandardError; end
         
     | 
| 
      
 15 
     | 
    
         
            +
                class UndefinedFunctionError < StandardError; end
         
     | 
| 
      
 16 
     | 
    
         
            +
                class UndefinedVariableError < StandardError; end
         
     | 
| 
      
 17 
     | 
    
         
            +
                class UnmodifiableError < StandardError; end
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Keisan
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Function
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :name
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize(name, function_proc)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  raise Keisan::Exceptions::InvalidFunctionError.new unless function_proc.is_a?(Proc)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @name = name
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @function_proc = function_proc
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def call(context, *args)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @function_proc.call(*args)
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,58 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative "rand"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require_relative "sample"
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Keisan
         
     | 
| 
      
 5 
     | 
    
         
            +
              module Functions
         
     | 
| 
      
 6 
     | 
    
         
            +
                class DefaultRegistry
         
     | 
| 
      
 7 
     | 
    
         
            +
                  def self.registry
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @registry ||= Registry.new.tap do |r|
         
     | 
| 
      
 9 
     | 
    
         
            +
                      register_defaults!(r)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    end.freeze
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  private
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  def self.register_defaults!(registry)
         
     | 
| 
      
 16 
     | 
    
         
            +
                    register_builtin_math!(registry)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    register_branch_methods!(registry)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    register_array_methods!(registry)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    register_random_methods!(registry)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  def self.register_builtin_math!(registry)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    Math.methods(false).each do |method|
         
     | 
| 
      
 24 
     | 
    
         
            +
                      registry.register!(
         
     | 
| 
      
 25 
     | 
    
         
            +
                        method,
         
     | 
| 
      
 26 
     | 
    
         
            +
                        Proc.new do |*args|
         
     | 
| 
      
 27 
     | 
    
         
            +
                          Math.send(method, *args)
         
     | 
| 
      
 28 
     | 
    
         
            +
                        end
         
     | 
| 
      
 29 
     | 
    
         
            +
                      )
         
     | 
| 
      
 30 
     | 
    
         
            +
                    end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                    %i(exp sin cos).each do |method|
         
     | 
| 
      
 33 
     | 
    
         
            +
                      registry.register!(
         
     | 
| 
      
 34 
     | 
    
         
            +
                        :"c#{method}",
         
     | 
| 
      
 35 
     | 
    
         
            +
                        Proc.new do |z|
         
     | 
| 
      
 36 
     | 
    
         
            +
                          CMath.send(method, z)
         
     | 
| 
      
 37 
     | 
    
         
            +
                        end
         
     | 
| 
      
 38 
     | 
    
         
            +
                      )
         
     | 
| 
      
 39 
     | 
    
         
            +
                    end
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  def self.register_branch_methods!(registry)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    registry.register!(:if, Proc.new {|bool, a, b=nil| bool ? a : b })
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  def self.register_array_methods!(registry)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    %i(min max size).each do |method|
         
     | 
| 
      
 48 
     | 
    
         
            +
                      registry.register!(method, Proc.new {|a| a.send(method)})
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                  def self.register_random_methods!(registry)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    registry.register!(:rand, Keisan::Functions::Rand.new)
         
     | 
| 
      
 54 
     | 
    
         
            +
                    registry.register!(:sample, Keisan::Functions::Sample.new)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
            end
         
     |