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,9 @@
1
+ module Keisan
2
+ module AST
3
+ class BitwiseOperator < Operator
4
+ def associativity
5
+ :left
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ module Keisan
2
+ module AST
3
+ class BitwiseOr < BitwiseOperator
4
+ def self.priority
5
+ 11
6
+ end
7
+
8
+ def arity
9
+ 2..Float::INFINITY
10
+ end
11
+
12
+ def symbol
13
+ :"|"
14
+ end
15
+
16
+ def blank_value
17
+ 0
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module Keisan
2
+ module AST
3
+ class BitwiseXor < BitwiseOperator
4
+ def self.priority
5
+ 21
6
+ end
7
+
8
+ def arity
9
+ 2..Float::INFINITY
10
+ end
11
+
12
+ def symbol
13
+ :"^"
14
+ end
15
+
16
+ def blank_value
17
+ 0
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ module Keisan
2
+ module AST
3
+ class Boolean < Literal
4
+ attr_reader :bool
5
+
6
+ def initialize(bool)
7
+ @bool = bool
8
+ end
9
+
10
+ def value(context = nil)
11
+ bool
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,141 @@
1
+ module Keisan
2
+ module AST
3
+ class Builder
4
+ # Build from parser
5
+ def initialize(string: nil, parser: nil, components: nil)
6
+ if string.nil? && parser.nil? && components.nil?
7
+ raise Keisan::Exceptions::InternalError.new("Require parser or components")
8
+ end
9
+
10
+ if string.present?
11
+ @components = Keisan::Parser.new(string: string).components
12
+ elsif parser.present?
13
+ @components = parser.components
14
+ else
15
+ @components = Array.wrap(components)
16
+ end
17
+
18
+ @nodes = @components.split {|component|
19
+ component.is_a?(Keisan::Parsing::Operator)
20
+ }.map {|group_of_components|
21
+ node_from_components(group_of_components)
22
+ }
23
+ @operators = @components.select {|component| component.is_a?(Keisan::Parsing::Operator)}
24
+
25
+ @priorities = @operators.map(&:priority)
26
+
27
+ while @operators.count > 0
28
+ priorities = @operators.map(&:priority)
29
+ max_priority = priorities.uniq.max
30
+ consume_operators_with_priority!(max_priority)
31
+ end
32
+
33
+ unless @nodes.count == 1
34
+ raise Keisan::Exceptions::ASTError.new("Should end up with a single node")
35
+ end
36
+ end
37
+
38
+ def node
39
+ @nodes.first
40
+ end
41
+
42
+ def ast
43
+ node
44
+ end
45
+
46
+ private
47
+
48
+ def node_from_components(components)
49
+ index_of_unary_components = components.map.with_index {|c,i| [c,i]}.select {|c,i| c.is_a?(Keisan::Parsing::UnaryOperator)}.map(&:last)
50
+ # Must be all in the front
51
+ unless index_of_unary_components.map.with_index.all? {|i,j| i == j}
52
+ raise Keisan::Exceptions::ASTError.new("unary operators must be in front")
53
+ end
54
+
55
+ index_of_indexing_components = components.map.with_index {|c,i| [c,i]}.select {|c,i| c.is_a?(Keisan::Parsing::Indexing)}.map(&:last)
56
+ unless index_of_indexing_components.reverse.map.with_index.all? {|i,j| i + j == components.size - 1 }
57
+ raise Keisan::Exceptions::ASTError.new("indexing components must be in back")
58
+ end
59
+
60
+ num_unary = index_of_unary_components.size
61
+ num_indexing = index_of_indexing_components.size
62
+
63
+ unless num_unary + 1 + num_indexing == components.size
64
+ raise Keisan::Exceptions::ASTError.new("have too many components")
65
+ end
66
+
67
+ unary_components = index_of_unary_components.map {|i| components[i]}
68
+ indexing_components = index_of_indexing_components.map {|i| components[i]}
69
+
70
+ node = node_of_component(components[unary_components.size])
71
+
72
+ indexing_components.each do |indexing_component|
73
+ node = indexing_component.node_class.new(
74
+ node,
75
+ indexing_component.arguments.map {|parsing_argument|
76
+ Builder.new(components: parsing_argument.components).node
77
+ }
78
+ )
79
+ end
80
+
81
+ unary_components.reverse.each do |unary_component|
82
+ node = unary_component.node_class.new(node)
83
+ end
84
+
85
+ node
86
+ end
87
+
88
+ def node_of_component(component)
89
+ case component
90
+ when Keisan::Parsing::Number
91
+ Keisan::AST::Number.new(component.value)
92
+ when Keisan::Parsing::String
93
+ Keisan::AST::String.new(component.value)
94
+ when Keisan::Parsing::Null
95
+ Keisan::AST::Null.new
96
+ when Keisan::Parsing::Variable
97
+ Keisan::AST::Variable.new(component.name)
98
+ when Keisan::Parsing::Boolean
99
+ Keisan::AST::Boolean.new(component.value)
100
+ when Keisan::Parsing::List
101
+ Keisan::AST::List.new(
102
+ component.arguments.map {|parsing_argument|
103
+ Builder.new(components: parsing_argument.components).node
104
+ }
105
+ )
106
+ when Keisan::Parsing::Group
107
+ Builder.new(components: component.components).node
108
+ when Keisan::Parsing::Function
109
+ Keisan::AST::Function.new(
110
+ component.arguments.map {|parsing_argument|
111
+ Builder.new(components: parsing_argument.components).node
112
+ },
113
+ component.name
114
+ )
115
+ else
116
+ raise Keisan::Exceptions::ASTError.new("Unhandled component, #{component}")
117
+ end
118
+ end
119
+
120
+ def consume_operators_with_priority!(priority)
121
+ # Treat back-to-back operators with same priority as one single call (e.g. 1 + 2 + 3 is add(1,2,3))
122
+ while @operators.any? {|operator| operator.priority == priority}
123
+ next_operator_group = @operators.each.with_index.to_a.split {|operator,i| operator.priority != priority}.select(&:present?).first
124
+ operator_group_indexes = next_operator_group.map(&:last)
125
+
126
+ first_index = operator_group_indexes.first
127
+ last_index = operator_group_indexes.last
128
+
129
+ replacement_node = next_operator_group.first.first.node_class.new(
130
+ children = @nodes[first_index..(last_index+1)],
131
+ parsing_operators = @operators[first_index..last_index]
132
+ )
133
+
134
+ @nodes.delete_if.with_index {|node, i| i >= first_index && i <= last_index+1}
135
+ @operators.delete_if.with_index {|node, i| i >= first_index && i <= last_index}
136
+ @nodes.insert(first_index, replacement_node)
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,25 @@
1
+ module Keisan
2
+ module AST
3
+ class Exponent < ArithmeticOperator
4
+ def self.priority
5
+ 30
6
+ end
7
+
8
+ def arity
9
+ (2..2)
10
+ end
11
+
12
+ def associativity
13
+ :right
14
+ end
15
+
16
+ def symbol
17
+ :**
18
+ end
19
+
20
+ def blank_value
21
+ 1
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ module Keisan
2
+ module AST
3
+ class Function < Parent
4
+ attr_reader :name
5
+
6
+ def initialize(arguments = [], name)
7
+ @name = name
8
+ super(arguments)
9
+ end
10
+
11
+ def value(context = nil)
12
+ context = Keisan::Context.new if context.nil?
13
+ argument_values = children.map {|child| child.value(context)}
14
+ function = context.function(name)
15
+ function.call(context, *argument_values)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+ module Keisan
2
+ module AST
3
+ class Indexing < UnaryOperator
4
+ attr_reader :arguments
5
+
6
+ def initialize(child, arguments = [])
7
+ @children = [child]
8
+ @arguments = arguments
9
+ end
10
+
11
+ def value(context = nil)
12
+ return children.first.value(context).send(:[], *arguments.map {|arg| arg.value(context)})
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ module Keisan
2
+ module AST
3
+ class List < Parent
4
+ def value(context = nil)
5
+ context = Keisan::Context.new if context.nil?
6
+ children.map {|child| child.value(context)}
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,6 @@
1
+ module Keisan
2
+ module AST
3
+ class Literal < Node
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,21 @@
1
+ module Keisan
2
+ module AST
3
+ class LogicalAnd < LogicalOperator
4
+ def self.priority
5
+ 22
6
+ end
7
+
8
+ def arity
9
+ 2..Float::INFINITY
10
+ end
11
+
12
+ def symbol
13
+ :"&"
14
+ end
15
+
16
+ def blank_value
17
+ true
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ module Keisan
2
+ module AST
3
+ class LogicalGreaterThan < LogicalOperator
4
+ def self.priority
5
+ 82
6
+ end
7
+
8
+ def arity
9
+ 2..2
10
+ end
11
+
12
+ def value(context = nil)
13
+ children.first.value(context) > children.last.value(context)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Keisan
2
+ module AST
3
+ class LogicalGreaterThanOrEqualTo < LogicalOperator
4
+ def self.priority
5
+ 62
6
+ end
7
+
8
+ def arity
9
+ 2..2
10
+ end
11
+
12
+ def value(context = nil)
13
+ children.first.value(context) >= children.last.value(context)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Keisan
2
+ module AST
3
+ class LogicalLessThan < LogicalOperator
4
+ def self.priority
5
+ 72
6
+ end
7
+
8
+ def arity
9
+ 2..2
10
+ end
11
+
12
+ def value(context = nil)
13
+ children.first.value(context) < children.last.value(context)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Keisan
2
+ module AST
3
+ class LogicalLessThanOrEqualTo < LogicalOperator
4
+ def self.priority
5
+ 52
6
+ end
7
+
8
+ def arity
9
+ 2..2
10
+ end
11
+
12
+ def value(context = nil)
13
+ children.first.value(context) <= children.last.value(context)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module Keisan
2
+ module AST
3
+ class LogicalOperator < Operator
4
+ def associativity
5
+ :left
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ module Keisan
2
+ module AST
3
+ class LogicalOr < LogicalOperator
4
+ def self.priority
5
+ 12
6
+ end
7
+
8
+ def arity
9
+ 2..Float::INFINITY
10
+ end
11
+
12
+ def symbol
13
+ :"|"
14
+ end
15
+
16
+ def blank_value
17
+ false
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ module Keisan
2
+ module AST
3
+ class Node
4
+ def value(context = nil)
5
+ raise Keisan::Exceptions::NotImplementedError.new
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ module Keisan
2
+ module AST
3
+ class Null < Literal
4
+ def initialize
5
+ end
6
+
7
+ def value(context = nil)
8
+ nil
9
+ end
10
+ end
11
+ end
12
+ end