ExpressionInterpreter 0.0.1

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.
data/README ADDED
File without changes
@@ -0,0 +1,229 @@
1
+ module ExpressionInterpreter
2
+
3
+ class Expression
4
+ def initialize(expression)
5
+ parser = Parser.new(expression)
6
+ @operation = parser.operation
7
+ @variables = parser.variables
8
+ end
9
+
10
+ def evaluate(&var)
11
+ context = Context.new
12
+ @variables.each do |variable|
13
+ context.assign(variable, yield(variable.name))
14
+ end
15
+ @operation.evaluate(context)
16
+ end
17
+
18
+ def to_s
19
+ @operation.to_s
20
+ end
21
+ end
22
+
23
+ class Operation
24
+ def initialize(left_operand, operation, right_operand)
25
+ @left_operand = left_operand
26
+ @operation = operation
27
+ @right_operand = right_operand
28
+ end
29
+
30
+ def evaluate(conext)
31
+ left_eval = @left_operand.evaluate(conext)
32
+ right_eval = @right_operand.evaluate(conext)
33
+ left_eval.send(@operation, right_eval)
34
+ end
35
+
36
+ def to_s
37
+ "(#{@left_operand}#{@operation}#{@right_operand})"
38
+ end
39
+ end
40
+
41
+ class CompositeOperation
42
+ attr_reader :replaced
43
+
44
+ def initialize
45
+ @operands = []
46
+ @operations = []
47
+ @replaced = ""
48
+ end
49
+
50
+ def add_operation(operand, operation)
51
+ @operands << operand.operation
52
+ @operations << operation.name
53
+ @replaced += operand.replaced
54
+ @replaced += operation.replaced
55
+ end
56
+
57
+ def last_operation(operand)
58
+ @operands << operand.operation
59
+ @replaced += operand.replaced
60
+ end
61
+
62
+ def operation
63
+ %w(* / + -).each do |op_match|
64
+ i = 0
65
+ retry if @operations.detect do |operation|
66
+ if op_match == operation
67
+ @operands[i] = Operation.new(@operands[i], @operations[i], @operands[i+1])
68
+ @operands.delete_at(i+1)
69
+ @operations.delete_at(i)
70
+ true
71
+ else
72
+ i += 1
73
+ false
74
+ end
75
+ end
76
+ end
77
+ @operands[0]
78
+ end
79
+
80
+ def to_s
81
+ end
82
+ end
83
+
84
+ class Variable
85
+ attr :name
86
+ def initialize(name)
87
+ @name = name.to_sym
88
+ end
89
+ def evaluate(conext)
90
+ conext.look_up(@name)
91
+ end
92
+ def to_s
93
+ @name.to_s
94
+ end
95
+ end
96
+
97
+ class Constant
98
+ def initialize(value)
99
+ @value = value.to_f
100
+ end
101
+ def evaluate(context)
102
+ @value
103
+ end
104
+ def to_s
105
+ @value.to_s
106
+ end
107
+ end
108
+
109
+ class Context
110
+ def initialize
111
+ @var_map = {}
112
+ end
113
+ def look_up(name)
114
+ @var_map[name]
115
+ end
116
+ def assign(variable, value)
117
+ @var_map[variable.name] = value.to_f
118
+ end
119
+ end
120
+
121
+ class Parser
122
+ attr_reader :operation, :variables
123
+
124
+ def initialize(expression)
125
+ @variables = []
126
+ expression.gsub!(/\s/, "")
127
+ @operation = parse(expression).operation
128
+ end
129
+
130
+ def parse(expression)
131
+ expression = strip_expression(expression)
132
+
133
+ cp = CompositeOperation.new
134
+ 1.times do
135
+
136
+ # Determine left operand
137
+ operand = next_operand(expression)
138
+ expression = expression[operand.replaced.size..-1]
139
+
140
+ # Determine operator
141
+ case expression
142
+ when /^([\+|\-|\*|\/])/
143
+ operator = Operator.new($1, $&)
144
+ expression.sub!($&, "")
145
+ cp.add_operation(operand, operator)
146
+ retry
147
+ else
148
+ cp.last_operation(operand)
149
+ end
150
+
151
+ end
152
+
153
+ Operand.new(cp.operation, cp.replaced)
154
+ end
155
+
156
+ def next_operand(expression)
157
+ start_with_open_bracket = /^\(/
158
+ start_with_number = /^([0-9]+|[0-9]*\.[0-9]+)/
159
+ start_with_operand = /^([^(\+|\-|\*|\/)]*)/
160
+
161
+ case expression
162
+ when start_with_open_bracket
163
+ # The whole expression within the open bracket and
164
+ # close bracket are the operand
165
+ replacant = find_expression(expression)
166
+ operand = parse(replacant).operation
167
+ when start_with_number
168
+ replacant = $&;
169
+ operand = Constant.new($1)
170
+ when start_with_operand
171
+ replacant = $&;
172
+ operand = Variable.new($1)
173
+ add_variable(operand)
174
+ end
175
+ Operand.new(operand, replacant)
176
+ end
177
+
178
+ def find_expression(exp)
179
+ pos, bc = 0, 0
180
+ exp.each_byte do |b|
181
+ pos += 1
182
+ bc += 1 if b == 40
183
+ bc -= 1 if b == 41
184
+ break if bc == 0
185
+ end
186
+ exp[0..pos-1]
187
+ end
188
+
189
+ # Strip open and close brackets belonging to each other
190
+ # at the begining and end. i.e.: (x+y) -> x+y
191
+ def strip_expression(expression)
192
+ 1.times do
193
+ if expression =~ /^\(/ && expression == find_expression(expression)
194
+ expression = expression.sub(/^\(/, "").sub(/\)$/, "")
195
+ retry
196
+ end
197
+ end
198
+ expression
199
+ end
200
+
201
+ def add_variable(variable)
202
+ unless @variables.detect {|var| var == variable }
203
+ @variables << variable
204
+ end
205
+ end
206
+
207
+ end
208
+
209
+ class Operand
210
+ attr_accessor :operation, :replaced
211
+ def initialize(operation, replaced)
212
+ @operation = operation
213
+ @replaced = replaced
214
+ end
215
+ end
216
+
217
+ class Operator
218
+ attr_accessor :name, :replaced
219
+ def initialize(name, replaced)
220
+ @name = name
221
+ @replaced = replaced
222
+ end
223
+ end
224
+
225
+ end
226
+
227
+
228
+
229
+
@@ -0,0 +1,51 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+
3
+ require 'expression_interpreter'
4
+ require 'test/unit'
5
+
6
+ include ExpressionInterpreter
7
+
8
+ class TestExpressionInterpreter < Test::Unit::TestCase
9
+
10
+ def setup
11
+ @context = {
12
+ :x1 => 12,
13
+ :x2 => 3,
14
+ :x3 => 90.0,
15
+ :x4 => 3,
16
+ :x5 => 10.0}
17
+ end
18
+
19
+ def test_add
20
+ exp = Expression.new(" x1 + x2")
21
+ assert_equal "(x1+x2)", exp.to_s
22
+ assert_equal 15, exp.evaluate {|v| @context[v] }
23
+ end
24
+
25
+ def test_sub
26
+ exp = Expression.new("x2 -x3")
27
+ assert_equal "(x2-x3)", exp.to_s
28
+ assert_equal -87, exp.evaluate {|v| @context[v] }
29
+ end
30
+
31
+ def test_operator_precedence
32
+ exp = Expression.new("x1 + x2 * x3")
33
+ assert_equal "(x1+(x2*x3))", exp.to_s
34
+ assert_equal 282, exp.evaluate {|v| @context[v] }
35
+ end
36
+
37
+ def test_respect_bracket
38
+ exp = Expression.new("(x1 + x2) * x3")
39
+ assert_equal "((x1+x2)*x3)", exp.to_s
40
+ assert_equal 1350, exp.evaluate {|v| @context[v] }
41
+ end
42
+
43
+ def test_formula1
44
+ exp = Expression.new("x1 * x2 + x3 * x4 / 10.0")
45
+ assert_equal "((x1*x2)+((x3*x4)/10.0))", exp.to_s
46
+ assert_equal 63, exp.evaluate {|v| @context[v] }
47
+ end
48
+
49
+ end
50
+
51
+
metadata ADDED
@@ -0,0 +1,48 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.0
3
+ specification_version: 1
4
+ name: ExpressionInterpreter
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.0.1
7
+ date: 2006-09-28 00:00:00 +02:00
8
+ summary: A simple expression interpreter
9
+ require_paths:
10
+ - lib
11
+ email: s.grijpink@gmail.com
12
+ homepage:
13
+ rubyforge_project:
14
+ description:
15
+ autorequire: expression_parser
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Sjors Grijpink
31
+ files:
32
+ - tests/test_parser.rb
33
+ - lib/expression_interpreter.rb
34
+ - README
35
+ test_files: []
36
+
37
+ rdoc_options: []
38
+
39
+ extra_rdoc_files:
40
+ - README
41
+ executables: []
42
+
43
+ extensions: []
44
+
45
+ requirements: []
46
+
47
+ dependencies: []
48
+