keisan 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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