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.
Files changed (112) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +6 -0
  5. data/Gemfile +4 -0
  6. data/MIT-LICENSE +19 -0
  7. data/README.md +188 -0
  8. data/Rakefile +6 -0
  9. data/bin/console +10 -0
  10. data/bin/setup +8 -0
  11. data/keisan.gemspec +31 -0
  12. data/lib/keisan.rb +118 -0
  13. data/lib/keisan/ast/arithmetic_operator.rb +9 -0
  14. data/lib/keisan/ast/bitwise_and.rb +21 -0
  15. data/lib/keisan/ast/bitwise_operator.rb +9 -0
  16. data/lib/keisan/ast/bitwise_or.rb +21 -0
  17. data/lib/keisan/ast/bitwise_xor.rb +21 -0
  18. data/lib/keisan/ast/boolean.rb +15 -0
  19. data/lib/keisan/ast/builder.rb +141 -0
  20. data/lib/keisan/ast/exponent.rb +25 -0
  21. data/lib/keisan/ast/function.rb +19 -0
  22. data/lib/keisan/ast/indexing.rb +16 -0
  23. data/lib/keisan/ast/list.rb +10 -0
  24. data/lib/keisan/ast/literal.rb +6 -0
  25. data/lib/keisan/ast/logical_and.rb +21 -0
  26. data/lib/keisan/ast/logical_greater_than.rb +17 -0
  27. data/lib/keisan/ast/logical_greater_than_or_equal_to.rb +17 -0
  28. data/lib/keisan/ast/logical_less_than.rb +17 -0
  29. data/lib/keisan/ast/logical_less_than_or_equal_to.rb +17 -0
  30. data/lib/keisan/ast/logical_operator.rb +9 -0
  31. data/lib/keisan/ast/logical_or.rb +21 -0
  32. data/lib/keisan/ast/node.rb +9 -0
  33. data/lib/keisan/ast/null.rb +12 -0
  34. data/lib/keisan/ast/number.rb +15 -0
  35. data/lib/keisan/ast/operator.rb +50 -0
  36. data/lib/keisan/ast/parent.rb +15 -0
  37. data/lib/keisan/ast/plus.rb +49 -0
  38. data/lib/keisan/ast/string.rb +15 -0
  39. data/lib/keisan/ast/times.rb +36 -0
  40. data/lib/keisan/ast/unary_bitwise_not.rb +9 -0
  41. data/lib/keisan/ast/unary_identity.rb +9 -0
  42. data/lib/keisan/ast/unary_inverse.rb +9 -0
  43. data/lib/keisan/ast/unary_logical_not.rb +9 -0
  44. data/lib/keisan/ast/unary_minus.rb +9 -0
  45. data/lib/keisan/ast/unary_operator.rb +13 -0
  46. data/lib/keisan/ast/unary_plus.rb +9 -0
  47. data/lib/keisan/ast/variable.rb +16 -0
  48. data/lib/keisan/calculator.rb +30 -0
  49. data/lib/keisan/context.rb +36 -0
  50. data/lib/keisan/exceptions.rb +19 -0
  51. data/lib/keisan/function.rb +15 -0
  52. data/lib/keisan/functions/default_registry.rb +58 -0
  53. data/lib/keisan/functions/rand.rb +22 -0
  54. data/lib/keisan/functions/registry.rb +50 -0
  55. data/lib/keisan/functions/sample.rb +20 -0
  56. data/lib/keisan/parser.rb +211 -0
  57. data/lib/keisan/parsing/argument.rb +6 -0
  58. data/lib/keisan/parsing/arithmetic_operator.rb +6 -0
  59. data/lib/keisan/parsing/bitwise_and.rb +9 -0
  60. data/lib/keisan/parsing/bitwise_not.rb +9 -0
  61. data/lib/keisan/parsing/bitwise_not_not.rb +9 -0
  62. data/lib/keisan/parsing/bitwise_operator.rb +6 -0
  63. data/lib/keisan/parsing/bitwise_or.rb +9 -0
  64. data/lib/keisan/parsing/bitwise_xor.rb +9 -0
  65. data/lib/keisan/parsing/boolean.rb +11 -0
  66. data/lib/keisan/parsing/component.rb +6 -0
  67. data/lib/keisan/parsing/divide.rb +9 -0
  68. data/lib/keisan/parsing/element.rb +6 -0
  69. data/lib/keisan/parsing/exponent.rb +9 -0
  70. data/lib/keisan/parsing/function.rb +12 -0
  71. data/lib/keisan/parsing/group.rb +11 -0
  72. data/lib/keisan/parsing/indexing.rb +14 -0
  73. data/lib/keisan/parsing/list.rb +10 -0
  74. data/lib/keisan/parsing/logical_and.rb +9 -0
  75. data/lib/keisan/parsing/logical_greater_than.rb +9 -0
  76. data/lib/keisan/parsing/logical_greater_than_or_equal_to.rb +9 -0
  77. data/lib/keisan/parsing/logical_less_than.rb +9 -0
  78. data/lib/keisan/parsing/logical_less_than_or_equal_to.rb +9 -0
  79. data/lib/keisan/parsing/logical_not.rb +9 -0
  80. data/lib/keisan/parsing/logical_not_not.rb +9 -0
  81. data/lib/keisan/parsing/logical_operator.rb +6 -0
  82. data/lib/keisan/parsing/logical_or.rb +9 -0
  83. data/lib/keisan/parsing/minus.rb +9 -0
  84. data/lib/keisan/parsing/null.rb +6 -0
  85. data/lib/keisan/parsing/number.rb +10 -0
  86. data/lib/keisan/parsing/operator.rb +13 -0
  87. data/lib/keisan/parsing/plus.rb +9 -0
  88. data/lib/keisan/parsing/round_group.rb +6 -0
  89. data/lib/keisan/parsing/square_group.rb +6 -0
  90. data/lib/keisan/parsing/string.rb +10 -0
  91. data/lib/keisan/parsing/times.rb +9 -0
  92. data/lib/keisan/parsing/unary_minus.rb +9 -0
  93. data/lib/keisan/parsing/unary_operator.rb +9 -0
  94. data/lib/keisan/parsing/unary_plus.rb +9 -0
  95. data/lib/keisan/parsing/variable.rb +11 -0
  96. data/lib/keisan/token.rb +25 -0
  97. data/lib/keisan/tokenizer.rb +41 -0
  98. data/lib/keisan/tokens/arithmetic_operator.rb +29 -0
  99. data/lib/keisan/tokens/bitwise_operator.rb +29 -0
  100. data/lib/keisan/tokens/boolean.rb +20 -0
  101. data/lib/keisan/tokens/comma.rb +11 -0
  102. data/lib/keisan/tokens/group.rb +28 -0
  103. data/lib/keisan/tokens/logical_operator.rb +38 -0
  104. data/lib/keisan/tokens/null.rb +15 -0
  105. data/lib/keisan/tokens/number.rb +24 -0
  106. data/lib/keisan/tokens/operator.rb +9 -0
  107. data/lib/keisan/tokens/string.rb +15 -0
  108. data/lib/keisan/tokens/word.rb +11 -0
  109. data/lib/keisan/variables/default_registry.rb +20 -0
  110. data/lib/keisan/variables/registry.rb +41 -0
  111. data/lib/keisan/version.rb +3 -0
  112. metadata +238 -0
@@ -0,0 +1,15 @@
1
+ module Keisan
2
+ module AST
3
+ class Number < Literal
4
+ attr_reader :number
5
+
6
+ def initialize(number)
7
+ @number = number
8
+ end
9
+
10
+ def value(context = nil)
11
+ number
12
+ end
13
+ end
14
+ end
15
+ end
@@ -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,15 @@
1
+ module Keisan
2
+ module AST
3
+ class String < Literal
4
+ attr_reader :content
5
+
6
+ def initialize(content)
7
+ @content = content
8
+ end
9
+
10
+ def value(context = nil)
11
+ content
12
+ end
13
+ end
14
+ end
15
+ 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,9 @@
1
+ module Keisan
2
+ module AST
3
+ class UnaryBitwiseNot < UnaryOperator
4
+ def value(context = nil)
5
+ return ~children.first.value(context)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Keisan
2
+ module AST
3
+ class UnaryIdentity < UnaryOperator
4
+ def value(context = nil)
5
+ return children.first.value(context)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Keisan
2
+ module AST
3
+ class UnaryInverse < UnaryOperator
4
+ def value(context = nil)
5
+ return Rational(1, children.first.value(context))
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Keisan
2
+ module AST
3
+ class UnaryLogicalNot < UnaryOperator
4
+ def value(context = nil)
5
+ return !children.first.value(context)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Keisan
2
+ module AST
3
+ class UnaryMinus < UnaryOperator
4
+ def value(context = nil)
5
+ return -1 * children.first.value(context)
6
+ end
7
+ end
8
+ end
9
+ 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,9 @@
1
+ module Keisan
2
+ module AST
3
+ class UnaryPlus < UnaryOperator
4
+ def value(context = nil)
5
+ return children.first.value(context)
6
+ end
7
+ end
8
+ end
9
+ 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