ExpressionInterpreter 0.0.1

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