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,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