hesabu 0.1.5 → 0.1.6
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/Gemfile.lock +1 -3
- data/README.md +2 -11
- data/bin/hesabucli +0 -0
- data/hesabu.gemspec +0 -2
- data/lib/hesabu/equation_cleaner.rb +11 -0
- data/lib/hesabu/errors.rb +8 -10
- data/lib/hesabu/solver.rb +26 -104
- data/lib/hesabu/version.rb +1 -1
- data/lib/hesabu.rb +4 -9
- metadata +4 -24
- data/lib/hesabu/interpreter.rb +0 -25
- data/lib/hesabu/parser.rb +0 -75
- data/lib/hesabu/types/float_lit.rb +0 -9
- data/lib/hesabu/types/fun_call.rb +0 -123
- data/lib/hesabu/types/indentifier_lit.rb +0 -9
- data/lib/hesabu/types/int_lit.rb +0 -9
- data/lib/hesabu/types/operation.rb +0 -42
- data/lib/hesabu/types/string_lit.rb +0 -9
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 431f06d479e890cf49a1cf8fb1f9a166b66a88aee4b6200be1c540996fecf4fd
         | 
| 4 | 
            +
              data.tar.gz: e6d8a83f61a8e986658b6c574c0b51c5365787819f0fb2bff9331b7cdf3ba07b
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: c22ea9e78df743ee0ea74d8fb3fc22291fe12fdd1cd7235d94e608c72627c665d9982c2e803eede67320cf42e453741682313d8a4aa87f59a449638d37187e8d
         | 
| 7 | 
            +
              data.tar.gz: 96761de748fed74701bc6aa799ec99ff309c77da03521111cd0a4960874552d7018bd5998c883cb2a41a6d826d2ec8fa3415b0ad70ff419c41c93f373370ddcd
         | 
    
        data/Gemfile.lock
    CHANGED
    
    | @@ -1,8 +1,7 @@ | |
| 1 1 | 
             
            PATH
         | 
| 2 2 | 
             
              remote: .
         | 
| 3 3 | 
             
              specs:
         | 
| 4 | 
            -
                hesabu (0.1. | 
| 5 | 
            -
                  parslet
         | 
| 4 | 
            +
                hesabu (0.1.5)
         | 
| 6 5 |  | 
| 7 6 | 
             
            GEM
         | 
| 8 7 | 
             
              remote: https://rubygems.org/
         | 
| @@ -34,7 +33,6 @@ GEM | |
| 34 33 | 
             
                parallel (1.12.1)
         | 
| 35 34 | 
             
                parser (2.5.1.0)
         | 
| 36 35 | 
             
                  ast (~> 2.4.0)
         | 
| 37 | 
            -
                parslet (1.8.2)
         | 
| 38 36 | 
             
                path_expander (1.0.2)
         | 
| 39 37 | 
             
                powerpack (0.1.1)
         | 
| 40 38 | 
             
                pronto (0.9.5)
         | 
    
        data/README.md
    CHANGED
    
    | @@ -25,13 +25,7 @@ The expressions can be more complex (excel like), see the supported functions [h | |
| 25 25 |  | 
| 26 26 | 
             
            Currently the solver is case sensitive (except function names)
         | 
| 27 27 |  | 
| 28 | 
            -
            Nb: Hesabu is swahili word for  | 
| 29 | 
            -
             | 
| 30 | 
            -
            ## Technical background
         | 
| 31 | 
            -
             | 
| 32 | 
            -
            * https://tomassetti.me/guide-parsing-algorithms-terminology/
         | 
| 33 | 
            -
            * https://github.com/PhilippeSigaud/Pegged/wiki/PEG-Basics
         | 
| 34 | 
            -
            * https://github.com/kschiess/parslet
         | 
| 28 | 
            +
            Nb: Hesabu is swahili word for arithmetic.
         | 
| 35 29 |  | 
| 36 30 | 
             
            ## Alternatives
         | 
| 37 31 |  | 
| @@ -62,10 +56,7 @@ chmod 0600 ~/.gem/credentials | |
| 62 56 |  | 
| 63 57 |  | 
| 64 58 | 
             
            ```
         | 
| 65 | 
            -
            gem bump
         | 
| 66 | 
            -
            gem build hesabu.gemspec
         | 
| 67 | 
            -
            gem push hesabu-x.x.x.gem
         | 
| 68 | 
            -
             | 
| 59 | 
            +
            gem bump --tag --release
         | 
| 69 60 | 
             
            ```
         | 
| 70 61 |  | 
| 71 62 |  | 
    
        data/bin/hesabucli
    ADDED
    
    | Binary file | 
    
        data/hesabu.gemspec
    CHANGED
    
    | @@ -23,8 +23,6 @@ Gem::Specification.new do |spec| | |
| 23 23 | 
             
              spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
         | 
| 24 24 | 
             
              spec.require_paths = ["lib"]
         | 
| 25 25 |  | 
| 26 | 
            -
              spec.add_dependency "parslet"
         | 
| 27 | 
            -
             | 
| 28 26 | 
             
              spec.add_development_dependency "bundler", "~> 1.16"
         | 
| 29 27 | 
             
              spec.add_development_dependency "rake", "~> 10.0"
         | 
| 30 28 | 
             
              spec.add_development_dependency "rspec", "~> 3.0"
         | 
    
        data/lib/hesabu/errors.rb
    CHANGED
    
    | @@ -1,17 +1,15 @@ | |
| 1 1 |  | 
| 2 2 | 
             
            module Hesabu
         | 
| 3 3 | 
             
              class Error < StandardError
         | 
| 4 | 
            +
                attr_accessor :errors
         | 
| 5 | 
            +
                def iniatialize(message)
         | 
| 6 | 
            +
                  super(message)
         | 
| 7 | 
            +
                  @errors = errors
         | 
| 8 | 
            +
                end
         | 
| 4 9 | 
             
              end
         | 
| 5 10 | 
             
              class ArgumentError < Error
         | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
              class CalculationError < Error
         | 
| 10 | 
            -
              end
         | 
| 11 | 
            -
              class DivideByZeroError < Error
         | 
| 12 | 
            -
              end
         | 
| 13 | 
            -
              class CyclicError < Error
         | 
| 14 | 
            -
              end
         | 
| 15 | 
            -
              class UnboundVariableError < Error
         | 
| 11 | 
            +
                def iniatialize(message)
         | 
| 12 | 
            +
                  super(message)
         | 
| 13 | 
            +
                end
         | 
| 16 14 | 
             
              end
         | 
| 17 15 | 
             
            end
         | 
    
        data/lib/hesabu/solver.rb
    CHANGED
    
    | @@ -1,127 +1,49 @@ | |
| 1 | 
            -
            module Hesabu
         | 
| 2 | 
            -
              class Solver
         | 
| 3 | 
            -
                include TSort
         | 
| 4 1 |  | 
| 5 | 
            -
                Equation = Struct.new(:name, :evaluable, :dependencies, :raw_expression)
         | 
| 6 | 
            -
                EMPTY_DEPENDENCIES = [].freeze
         | 
| 7 | 
            -
                FakeEvaluable = Struct.new(:eval)
         | 
| 8 2 |  | 
| 3 | 
            +
            module Hesabu
         | 
| 4 | 
            +
              class Solver
         | 
| 9 5 | 
             
                def initialize
         | 
| 10 | 
            -
                  @parser = ::Hesabu::Parser.new
         | 
| 11 | 
            -
                  @interpreter = ::Hesabu::Interpreter.new
         | 
| 12 6 | 
             
                  @equations = {}
         | 
| 13 | 
            -
                  @bindings = {}
         | 
| 14 7 | 
             
                end
         | 
| 15 8 |  | 
| 16 9 | 
             
                def add(name, raw_expression)
         | 
| 17 10 | 
             
                  if raw_expression.nil? || name.nil?
         | 
| 18 11 | 
             
                    raise Hesabu::ArgumentError, "name or expression can't be nil : '#{name}', '#{raw_expression}'"
         | 
| 19 12 | 
             
                  end
         | 
| 20 | 
            -
             | 
| 21 | 
            -
                  if ::Hesabu::Types.as_numeric(raw_expression)
         | 
| 22 | 
            -
                    add_numeric(name, raw_expression)
         | 
| 23 | 
            -
                  else
         | 
| 24 | 
            -
                    add_equation(name, raw_expression)
         | 
| 25 | 
            -
                  end
         | 
| 13 | 
            +
                  @equations[name] = EquationCleaner.clean(raw_expression.to_s)
         | 
| 26 14 | 
             
                end
         | 
| 27 15 |  | 
| 28 16 | 
             
                def solve!
         | 
| 29 | 
            -
                   | 
| 30 | 
            -
             | 
| 17 | 
            +
                  result = nil
         | 
| 18 | 
            +
                  IO.popen(HESABUCLI, mode = "r+") do |io|
         | 
| 19 | 
            +
                    io.write @equations.to_json
         | 
| 20 | 
            +
                    io.close_write # let the process know you've given it all the data
         | 
| 21 | 
            +
                    result = io.read
         | 
| 31 22 | 
             
                  end
         | 
| 32 | 
            -
                  solution =  | 
| 33 | 
            -
                   | 
| 34 | 
            -
                  to_numerics(solution)
         | 
| 35 | 
            -
                rescue StandardError => e
         | 
| 36 | 
            -
                  log_and_raise(e)
         | 
| 37 | 
            -
                end
         | 
| 23 | 
            +
                  solution = JSON.parse(result)
         | 
| 24 | 
            +
                  exit_status = $CHILD_STATUS.exitstatus
         | 
| 38 25 |  | 
| 39 | 
            -
             | 
| 40 | 
            -
                   | 
| 41 | 
            -
             | 
| 42 | 
            -
                  raise Hesabu::CyclicError, "There's a cycle between the variables : " + e.message[25..-1]
         | 
| 43 | 
            -
                end
         | 
| 44 | 
            -
             | 
| 45 | 
            -
                def tsort_each_node(&block)
         | 
| 46 | 
            -
                  @equations.each_key(&block)
         | 
| 47 | 
            -
                end
         | 
| 48 | 
            -
             | 
| 49 | 
            -
                def tsort_each_child(node, &block)
         | 
| 50 | 
            -
                  equation = @equations[node]
         | 
| 51 | 
            -
                  raise UnboundVariableError, unbound_message(node) unless equation
         | 
| 52 | 
            -
                  equation.dependencies.each(&block)
         | 
| 53 | 
            -
                end
         | 
| 54 | 
            -
             | 
| 55 | 
            -
                private
         | 
| 56 | 
            -
             | 
| 57 | 
            -
                def to_numerics(solution)
         | 
| 58 | 
            -
                  solution.each_with_object({}) do |kv, hash|
         | 
| 59 | 
            -
                    hash[kv.first] = Hesabu::Types.as_numeric(kv.last) || kv.last
         | 
| 60 | 
            -
                  end
         | 
| 26 | 
            +
                  log_everything(exit_status, result) if ENV["HESABU_DEBUG"] || exit_status != 0
         | 
| 27 | 
            +
                  handle_error(solution) if exit_status != 0
         | 
| 28 | 
            +
                  solution
         | 
| 61 29 | 
             
                end
         | 
| 62 30 |  | 
| 63 | 
            -
                def  | 
| 64 | 
            -
                   | 
| 65 | 
            -
                   | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
                def log_error(e)
         | 
| 69 | 
            -
                  log "Error during processing: #{$ERROR_INFO}"
         | 
| 70 | 
            -
                  log "Error : #{e.class} #{e.message}"
         | 
| 71 | 
            -
                  log "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
         | 
| 72 | 
            -
                end
         | 
| 73 | 
            -
             | 
| 74 | 
            -
                def evaluate_equation(equation)
         | 
| 75 | 
            -
                  raise "not evaluable #{equation.evaluable} #{equation}" unless equation.evaluable.respond_to?(:eval, false)
         | 
| 76 | 
            -
                  begin
         | 
| 77 | 
            -
                    @bindings[equation.name] = equation.evaluable.eval
         | 
| 78 | 
            -
                  rescue StandardError => e
         | 
| 79 | 
            -
                    raise CalculationError, "Failed to evaluate #{equation.name} due to #{e.message} in formula #{equation.raw_expression}"
         | 
| 80 | 
            -
                  end
         | 
| 81 | 
            -
                end
         | 
| 82 | 
            -
             | 
| 83 | 
            -
                def log(message)
         | 
| 84 | 
            -
                  puts message
         | 
| 85 | 
            -
                end
         | 
| 86 | 
            -
             | 
| 87 | 
            -
                def add_numeric(name, raw_expression)
         | 
| 88 | 
            -
                  @equations[name] = Equation.new(
         | 
| 89 | 
            -
                    name,
         | 
| 90 | 
            -
                    FakeEvaluable.new(::Hesabu::Types.as_bigdecimal(raw_expression)),
         | 
| 91 | 
            -
                    EMPTY_DEPENDENCIES,
         | 
| 92 | 
            -
                    raw_expression
         | 
| 93 | 
            -
                  )
         | 
| 94 | 
            -
                end
         | 
| 95 | 
            -
             | 
| 96 | 
            -
                def add_equation(name, raw_expression)
         | 
| 97 | 
            -
                  expression = raw_expression.gsub(/\r\n?/, "")
         | 
| 98 | 
            -
                  ast_tree = begin
         | 
| 99 | 
            -
                    @parser.parse(expression)
         | 
| 100 | 
            -
                  rescue Parslet::ParseFailed => e
         | 
| 101 | 
            -
                    log(raw_expression)
         | 
| 102 | 
            -
                    log_error(e)
         | 
| 103 | 
            -
                    raise ParseError, "failed to parse #{name} := #{expression} : #{e.message}"
         | 
| 104 | 
            -
                  end
         | 
| 105 | 
            -
                  var_identifiers = Set.new
         | 
| 106 | 
            -
                  interpretation = @interpreter.apply(
         | 
| 107 | 
            -
                    ast_tree,
         | 
| 108 | 
            -
                    doc:             @bindings,
         | 
| 109 | 
            -
                    var_identifiers: var_identifiers
         | 
| 110 | 
            -
                  )
         | 
| 111 | 
            -
                  if ENV["HESABU_DEBUG"]
         | 
| 112 | 
            -
                    log expression
         | 
| 113 | 
            -
                    log JSON.pretty_generate(ast_tree)
         | 
| 114 | 
            -
                  end
         | 
| 115 | 
            -
                  @equations[name] = Equation.new(name, interpretation, var_identifiers, raw_expression)
         | 
| 116 | 
            -
                end
         | 
| 31 | 
            +
                def handle_error(solution)
         | 
| 32 | 
            +
                  puts
         | 
| 33 | 
            +
                  error = solution["errors"].first
         | 
| 34 | 
            +
                  message = "In equation #{error['source']} " + error["message"] + " #{error['source']} := #{error['expression']}"
         | 
| 117 35 |  | 
| 118 | 
            -
             | 
| 119 | 
            -
                   | 
| 120 | 
            -
                   | 
| 36 | 
            +
                  err = Hesabu::Error.new(message)
         | 
| 37 | 
            +
                  err.errors = solution["errors"]
         | 
| 38 | 
            +
                  raise err
         | 
| 121 39 | 
             
                end
         | 
| 122 40 |  | 
| 123 | 
            -
                def  | 
| 124 | 
            -
                   | 
| 41 | 
            +
                def log_everything(exit_status, result)
         | 
| 42 | 
            +
                  puts ["**************",
         | 
| 43 | 
            +
                        "exit_status:#{exit_status}",
         | 
| 44 | 
            +
                        @equations.to_json,
         | 
| 45 | 
            +
                        "=> ",
         | 
| 46 | 
            +
                        result].join("\n")
         | 
| 125 47 | 
             
                end
         | 
| 126 48 | 
             
              end
         | 
| 127 49 | 
             
            end
         | 
    
        data/lib/hesabu/version.rb
    CHANGED
    
    
    
        data/lib/hesabu.rb
    CHANGED
    
    | @@ -1,17 +1,12 @@ | |
| 1 1 | 
             
            require "hesabu/version"
         | 
| 2 | 
            -
             | 
| 2 | 
            +
             | 
| 3 3 | 
             
            require_relative "./hesabu/errors"
         | 
| 4 | 
            -
            require_relative "./hesabu/parser"
         | 
| 5 4 | 
             
            require_relative "./hesabu/types/numeric"
         | 
| 6 | 
            -
            require_relative "./hesabu/types/float_lit"
         | 
| 7 | 
            -
            require_relative "./hesabu/types/fun_call"
         | 
| 8 | 
            -
            require_relative "./hesabu/types/indentifier_lit"
         | 
| 9 | 
            -
            require_relative "./hesabu/types/int_lit"
         | 
| 10 | 
            -
            require_relative "./hesabu/types/string_lit"
         | 
| 11 | 
            -
            require_relative "./hesabu/types/operation"
         | 
| 12 5 |  | 
| 13 | 
            -
            require_relative "./hesabu/ | 
| 6 | 
            +
            require_relative "./hesabu/equation_cleaner"
         | 
| 14 7 | 
             
            require_relative "./hesabu/solver"
         | 
| 15 8 |  | 
| 16 9 | 
             
            module Hesabu
         | 
| 10 | 
            +
              HESABUCLI = File.expand_path("../bin/hesabucli", File.dirname(__FILE__))
         | 
| 11 | 
            +
              puts "************** HESABU cli location : " + HESABUCLI
         | 
| 17 12 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,29 +1,15 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: hesabu
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.1. | 
| 4 | 
            +
              version: 0.1.6
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Stéphan Mestach
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2018-07- | 
| 11 | 
            +
            date: 2018-07-09 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 | 
            -
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            -
              name: parslet
         | 
| 15 | 
            -
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            -
                requirements:
         | 
| 17 | 
            -
                - - ">="
         | 
| 18 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            -
                    version: '0'
         | 
| 20 | 
            -
              type: :runtime
         | 
| 21 | 
            -
              prerelease: false
         | 
| 22 | 
            -
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            -
                requirements:
         | 
| 24 | 
            -
                - - ">="
         | 
| 25 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            -
                    version: '0'
         | 
| 27 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 28 14 | 
             
              name: bundler
         | 
| 29 15 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -171,20 +157,14 @@ files: | |
| 171 157 | 
             
            - Rakefile
         | 
| 172 158 | 
             
            - bin/console
         | 
| 173 159 | 
             
            - bin/fast
         | 
| 160 | 
            +
            - bin/hesabucli
         | 
| 174 161 | 
             
            - bin/setup
         | 
| 175 162 | 
             
            - hesabu.gemspec
         | 
| 176 163 | 
             
            - lib/hesabu.rb
         | 
| 164 | 
            +
            - lib/hesabu/equation_cleaner.rb
         | 
| 177 165 | 
             
            - lib/hesabu/errors.rb
         | 
| 178 | 
            -
            - lib/hesabu/interpreter.rb
         | 
| 179 | 
            -
            - lib/hesabu/parser.rb
         | 
| 180 166 | 
             
            - lib/hesabu/solver.rb
         | 
| 181 | 
            -
            - lib/hesabu/types/float_lit.rb
         | 
| 182 | 
            -
            - lib/hesabu/types/fun_call.rb
         | 
| 183 | 
            -
            - lib/hesabu/types/indentifier_lit.rb
         | 
| 184 | 
            -
            - lib/hesabu/types/int_lit.rb
         | 
| 185 167 | 
             
            - lib/hesabu/types/numeric.rb
         | 
| 186 | 
            -
            - lib/hesabu/types/operation.rb
         | 
| 187 | 
            -
            - lib/hesabu/types/string_lit.rb
         | 
| 188 168 | 
             
            - lib/hesabu/version.rb
         | 
| 189 169 | 
             
            homepage: https://github.com/BLSQ/hesabu
         | 
| 190 170 | 
             
            licenses:
         | 
    
        data/lib/hesabu/interpreter.rb
    DELETED
    
    | @@ -1,25 +0,0 @@ | |
| 1 | 
            -
            module Hesabu
         | 
| 2 | 
            -
              class Interpreter < Parslet::Transform
         | 
| 3 | 
            -
                rule(plist: sequence(:arr)) { arr }
         | 
| 4 | 
            -
                rule(plist: "()") { [] }
         | 
| 5 | 
            -
                rule(l: simple(:left),
         | 
| 6 | 
            -
                     r: simple(:right),
         | 
| 7 | 
            -
                     o: simple(:op)) do
         | 
| 8 | 
            -
                  Hesabu::Types::Operation.new(left, op, right)
         | 
| 9 | 
            -
                end
         | 
| 10 | 
            -
                rule(identifier: simple(:id)) { id.to_s }
         | 
| 11 | 
            -
                rule(variable: simple(:variable)) do |d|
         | 
| 12 | 
            -
                  d[:var_identifiers]&.add(d[:variable])
         | 
| 13 | 
            -
                  Hesabu::Types::IdentifierLit.new(d[:variable], d[:doc])
         | 
| 14 | 
            -
                end
         | 
| 15 | 
            -
                rule(fcall: { name: simple(:name), varlist: sequence(:vars) }) do
         | 
| 16 | 
            -
                  Hesabu::Types::FunCall.new(name, vars)
         | 
| 17 | 
            -
                end
         | 
| 18 | 
            -
                rule(str: subtree(:str)) do
         | 
| 19 | 
            -
                  Hesabu::Types::StringLit.new(str.map { |char| char.values.first.str }.join)
         | 
| 20 | 
            -
                end
         | 
| 21 | 
            -
             | 
| 22 | 
            -
                rule(integer: simple(:integer)) { Hesabu::Types::IntLit.new(integer) }
         | 
| 23 | 
            -
                rule(float: simple(:float)) { Hesabu::Types::FloatLit.new(float) }
         | 
| 24 | 
            -
              end
         | 
| 25 | 
            -
            end
         | 
    
        data/lib/hesabu/parser.rb
    DELETED
    
    | @@ -1,75 +0,0 @@ | |
| 1 | 
            -
            module Hesabu
         | 
| 2 | 
            -
              class Parser < Parslet::Parser
         | 
| 3 | 
            -
                def cts(atom_arg)
         | 
| 4 | 
            -
                  atom_arg >> space?
         | 
| 5 | 
            -
                end
         | 
| 6 | 
            -
             | 
| 7 | 
            -
                # simple things
         | 
| 8 | 
            -
                rule(:lparen)             { str("(") >> space? }
         | 
| 9 | 
            -
                rule(:rparen)             { str(")") >> space? }
         | 
| 10 | 
            -
                rule(:comma)              { str(",") >> space? }
         | 
| 11 | 
            -
                rule(:space)              { match["\s"] | match["\t"] | match["\n"] }
         | 
| 12 | 
            -
                rule(:spaces)             { space.repeat }
         | 
| 13 | 
            -
                rule(:space?)             { spaces.maybe }
         | 
| 14 | 
            -
             | 
| 15 | 
            -
                rule(:nonquote)   { str("'").absnt? >> any }
         | 
| 16 | 
            -
                rule(:quote)      { str("'") }
         | 
| 17 | 
            -
                rule(:string)     { quote >> nonquote.as(:char).repeat(1).as(:str) >> quote >> space? }
         | 
| 18 | 
            -
             | 
| 19 | 
            -
                rule(:identifier) do
         | 
| 20 | 
            -
                  cts((match["a-zA-Z"] >> match["a-zA-Z0-9_"].repeat).as(:identifier))
         | 
| 21 | 
            -
                end
         | 
| 22 | 
            -
             | 
| 23 | 
            -
                rule(:separator) { str(";") }
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                rule(:digit) { match["0-9"] }
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                rule(:integer) do
         | 
| 28 | 
            -
                  cts((str("-").maybe >> match["1-9"] >> digit.repeat).as(:integer) | str("0").as(:integer))
         | 
| 29 | 
            -
                end
         | 
| 30 | 
            -
             | 
| 31 | 
            -
                rule(:float) do
         | 
| 32 | 
            -
                  cts((str("-").maybe >> digit.repeat(1) >> str(".") >> digit.repeat(1)).as(:float)) |
         | 
| 33 | 
            -
                  cts((str(".") >> digit.repeat(1)).as(:float))
         | 
| 34 | 
            -
                end
         | 
| 35 | 
            -
             | 
| 36 | 
            -
                # arithmetic
         | 
| 37 | 
            -
             | 
| 38 | 
            -
                rule(:expression)         { iexpression | variable | pexpression }
         | 
| 39 | 
            -
                rule(:pexpression)        { lparen >> expression >> rparen }
         | 
| 40 | 
            -
             | 
| 41 | 
            -
                rule(:variable)           { identifier.as(:variable) }
         | 
| 42 | 
            -
                rule(:sum_op)             { match("[+-]") >> space? }
         | 
| 43 | 
            -
                rule(:mul_op)             { match("[*/]") >> space? }
         | 
| 44 | 
            -
                rule(:comparison_op)      do
         | 
| 45 | 
            -
                  (
         | 
| 46 | 
            -
                   str("<=") | str(">=") | str("==") |
         | 
| 47 | 
            -
                   str("!=") | str("<") | str("=") |
         | 
| 48 | 
            -
                   str(">") | str("AND")
         | 
| 49 | 
            -
                  ) >> space?
         | 
| 50 | 
            -
                end
         | 
| 51 | 
            -
             | 
| 52 | 
            -
                rule(:atom) { string | pexpression | float | integer | fcall.as(:fcall) | variable }
         | 
| 53 | 
            -
             | 
| 54 | 
            -
                rule(:iexpression) do
         | 
| 55 | 
            -
                  infix_expression(atom,
         | 
| 56 | 
            -
                                   [mul_op, 3, :left],
         | 
| 57 | 
            -
                                   [sum_op, 2, :left],
         | 
| 58 | 
            -
                                   [comparison_op, 1, :left])
         | 
| 59 | 
            -
                end
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                # lists
         | 
| 62 | 
            -
                rule(:varlist)    { expression >> (comma >> expression).repeat }
         | 
| 63 | 
            -
                rule(:pvarlist)   { (lparen >> varlist.repeat >> rparen).as(:plist) }
         | 
| 64 | 
            -
             | 
| 65 | 
            -
                # functions
         | 
| 66 | 
            -
                rule(:fcall)        { identifier.as(:name) >> pvarlist.as(:varlist) }
         | 
| 67 | 
            -
             | 
| 68 | 
            -
                # root
         | 
| 69 | 
            -
                rule(:command) do
         | 
| 70 | 
            -
                  iexpression | expression | atom
         | 
| 71 | 
            -
                end
         | 
| 72 | 
            -
                rule(:commands) { commands.repeat }
         | 
| 73 | 
            -
                root :command
         | 
| 74 | 
            -
              end
         | 
| 75 | 
            -
            end
         | 
| @@ -1,123 +0,0 @@ | |
| 1 | 
            -
            module Hesabu
         | 
| 2 | 
            -
              module Types
         | 
| 3 | 
            -
                class Function
         | 
| 4 | 
            -
                  def divide(num, denum)
         | 
| 5 | 
            -
                    num / denum
         | 
| 6 | 
            -
                  end
         | 
| 7 | 
            -
                end
         | 
| 8 | 
            -
                class IfFunction < Function
         | 
| 9 | 
            -
                  def call(args)
         | 
| 10 | 
            -
                    raise "expected args #{name} : #{args}" unless args.size != 2
         | 
| 11 | 
            -
                    condition_expression = args[0]
         | 
| 12 | 
            -
                    condition = condition_expression.eval
         | 
| 13 | 
            -
                    condition ? args[1].eval : args[2].eval
         | 
| 14 | 
            -
                  end
         | 
| 15 | 
            -
                end
         | 
| 16 | 
            -
             | 
| 17 | 
            -
                class SumFunction < Function
         | 
| 18 | 
            -
                  def call(args)
         | 
| 19 | 
            -
                    values = args.map(&:eval)
         | 
| 20 | 
            -
                    values.reduce(0, :+)
         | 
| 21 | 
            -
                  end
         | 
| 22 | 
            -
                end
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                class ScoreTableFunction < Function
         | 
| 25 | 
            -
                  def call(args)
         | 
| 26 | 
            -
                    values = args.map(&:eval)
         | 
| 27 | 
            -
                    target = values.shift
         | 
| 28 | 
            -
                    matching_rules = values.each_slice(3).find do |lower, greater, result|
         | 
| 29 | 
            -
                      greater.nil? || result.nil? ? true : lower <= target && target < greater
         | 
| 30 | 
            -
                    end
         | 
| 31 | 
            -
                    matching_rules.last
         | 
| 32 | 
            -
                  end
         | 
| 33 | 
            -
                end
         | 
| 34 | 
            -
             | 
| 35 | 
            -
                class AvgFunction < Function
         | 
| 36 | 
            -
                  def call(args)
         | 
| 37 | 
            -
                    values = args.map(&:eval)
         | 
| 38 | 
            -
                    values.inject(0.0) { |acc, elem| acc + elem } / values.size
         | 
| 39 | 
            -
                  end
         | 
| 40 | 
            -
                end
         | 
| 41 | 
            -
             | 
| 42 | 
            -
                class SafeDivFunction < Function
         | 
| 43 | 
            -
                  def call(args)
         | 
| 44 | 
            -
                    eval_denom = args[1].eval
         | 
| 45 | 
            -
                    if eval_denom == 0
         | 
| 46 | 
            -
                      0
         | 
| 47 | 
            -
                    else
         | 
| 48 | 
            -
                      eval_num = args[0].eval
         | 
| 49 | 
            -
                      eval_denom.zero? ? 0 : (eval_num / eval_denom)
         | 
| 50 | 
            -
                    end
         | 
| 51 | 
            -
                  end
         | 
| 52 | 
            -
                end
         | 
| 53 | 
            -
             | 
| 54 | 
            -
                class MinFunction
         | 
| 55 | 
            -
                  def call(args)
         | 
| 56 | 
            -
                    values = args.map(&:eval)
         | 
| 57 | 
            -
                    values.min
         | 
| 58 | 
            -
                  end
         | 
| 59 | 
            -
                end
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                class MaxFunction
         | 
| 62 | 
            -
                  def call(args)
         | 
| 63 | 
            -
                    values = args.map(&:eval)
         | 
| 64 | 
            -
                    values.max
         | 
| 65 | 
            -
                  end
         | 
| 66 | 
            -
                end
         | 
| 67 | 
            -
             | 
| 68 | 
            -
                class RandbetweenFunction
         | 
| 69 | 
            -
                  def call(args)
         | 
| 70 | 
            -
                    values = args.map(&:eval)
         | 
| 71 | 
            -
                    rand(values.first..values.last)
         | 
| 72 | 
            -
                  end
         | 
| 73 | 
            -
                end
         | 
| 74 | 
            -
             | 
| 75 | 
            -
                class AbsFunction
         | 
| 76 | 
            -
                  def call(args)
         | 
| 77 | 
            -
                    raise "expected args #{self.class.name} : #{args}" if args.size != 1
         | 
| 78 | 
            -
                    args.first.eval.abs
         | 
| 79 | 
            -
                  end
         | 
| 80 | 
            -
                end
         | 
| 81 | 
            -
             | 
| 82 | 
            -
                class AccessFunction
         | 
| 83 | 
            -
                  def call(args)
         | 
| 84 | 
            -
                    values = args.map(&:eval)
         | 
| 85 | 
            -
                    array = values[0..-2]
         | 
| 86 | 
            -
                    index = values[-1]
         | 
| 87 | 
            -
                    array[index]
         | 
| 88 | 
            -
                  end
         | 
| 89 | 
            -
                end
         | 
| 90 | 
            -
             | 
| 91 | 
            -
                class RoundFunction
         | 
| 92 | 
            -
                  def call(args)
         | 
| 93 | 
            -
                    raise "expected args #{self.class.name} : #{args}" if args.size > 2 || args.empty?
         | 
| 94 | 
            -
                    values = args.map(&:eval)
         | 
| 95 | 
            -
                    decimals = args.size == 2 ? values[1] : 0
         | 
| 96 | 
            -
                    values.first.round(decimals)
         | 
| 97 | 
            -
                  end
         | 
| 98 | 
            -
                end
         | 
| 99 | 
            -
             | 
| 100 | 
            -
                FUNCTIONS = {
         | 
| 101 | 
            -
                  "if"          => IfFunction.new,
         | 
| 102 | 
            -
                  "sum"         => SumFunction.new,
         | 
| 103 | 
            -
                  "avg"         => AvgFunction.new,
         | 
| 104 | 
            -
                  "min"         => MinFunction.new,
         | 
| 105 | 
            -
                  "max"         => MaxFunction.new,
         | 
| 106 | 
            -
                  "safe_div"    => SafeDivFunction.new,
         | 
| 107 | 
            -
                  "randbetween" => RandbetweenFunction.new,
         | 
| 108 | 
            -
                  "score_table" => ScoreTableFunction.new,
         | 
| 109 | 
            -
                  "abs"         => AbsFunction.new,
         | 
| 110 | 
            -
                  "access"      => AccessFunction.new,
         | 
| 111 | 
            -
                  "round"       => RoundFunction.new
         | 
| 112 | 
            -
                }.freeze
         | 
| 113 | 
            -
             | 
| 114 | 
            -
                FunCall = Struct.new(:name, :args) do
         | 
| 115 | 
            -
                  def eval
         | 
| 116 | 
            -
                    function_name = name.strip.downcase
         | 
| 117 | 
            -
                    function = FUNCTIONS[function_name]
         | 
| 118 | 
            -
                    raise "unsupported function call  : #{function_name} only knows #{FUNCTIONS.keys.join(', ')}" unless function
         | 
| 119 | 
            -
                    function.call(args)
         | 
| 120 | 
            -
                  end
         | 
| 121 | 
            -
                end
         | 
| 122 | 
            -
              end
         | 
| 123 | 
            -
            end
         | 
    
        data/lib/hesabu/types/int_lit.rb
    DELETED
    
    
| @@ -1,42 +0,0 @@ | |
| 1 | 
            -
            module Hesabu
         | 
| 2 | 
            -
              module Types
         | 
| 3 | 
            -
                Operation = Struct.new(:left, :operator, :right) do
         | 
| 4 | 
            -
                  def eval
         | 
| 5 | 
            -
                    op = operator.str.strip
         | 
| 6 | 
            -
                    result(op, left.eval, right.eval)
         | 
| 7 | 
            -
                  end
         | 
| 8 | 
            -
             | 
| 9 | 
            -
                  private
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                  def result(op, leftval, rightval)
         | 
| 12 | 
            -
                    case op
         | 
| 13 | 
            -
                    when "+"
         | 
| 14 | 
            -
                      leftval + rightval
         | 
| 15 | 
            -
                    when  "-"
         | 
| 16 | 
            -
                      leftval - rightval
         | 
| 17 | 
            -
                    when  "*"
         | 
| 18 | 
            -
                      leftval * rightval
         | 
| 19 | 
            -
                    when  "/"
         | 
| 20 | 
            -
                      raise DivideByZeroError, "division by 0 : #{leftval}/0" if rightval.zero?
         | 
| 21 | 
            -
                      leftval / rightval
         | 
| 22 | 
            -
                    when  ">"
         | 
| 23 | 
            -
                      leftval > rightval
         | 
| 24 | 
            -
                    when  "<"
         | 
| 25 | 
            -
                      leftval < rightval
         | 
| 26 | 
            -
                    when  ">="
         | 
| 27 | 
            -
                      leftval >= rightval
         | 
| 28 | 
            -
                    when  "<="
         | 
| 29 | 
            -
                      leftval <= rightval
         | 
| 30 | 
            -
                    when  "=", "=="
         | 
| 31 | 
            -
                      leftval == rightval
         | 
| 32 | 
            -
                    when  "!="
         | 
| 33 | 
            -
                      leftval != rightval
         | 
| 34 | 
            -
                    when  "AND"
         | 
| 35 | 
            -
                      leftval && rightval
         | 
| 36 | 
            -
                    else
         | 
| 37 | 
            -
                      raise "unsupported operand : #{op} : #{left} #{operator} #{right}"
         | 
| 38 | 
            -
                    end
         | 
| 39 | 
            -
                  end
         | 
| 40 | 
            -
                end
         | 
| 41 | 
            -
              end
         | 
| 42 | 
            -
            end
         |