math_engine 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # MathEngine
2
2
 
3
- MathEngine is a lightweight mathematical expression parser and evaluator. It currently handles addition, subtraction, multiplication and division, but support is planned for other operators and variables, etc.
3
+ MathEngine is a lightweight mathematical expression parser and evaluator. It currently handles addition, subtraction, multiplication, division and the use of variables.
4
4
 
5
5
  Install with
6
6
 
@@ -11,14 +11,18 @@ Install with
11
11
  require 'rubygems'
12
12
  require 'math_engine'
13
13
 
14
- puts "#{MathEngine.new.evaluate("10 * (3 * 2) + (55 - 5) / (2.5 * (3 + 1))")}"
14
+ engine = MathEngine.new
15
+
16
+ puts "#{engine.evaluate("x = 10 * (3 * 2) + (55 - 5) / (2.5 * (3 + 1))")}"
17
+ puts "#{engine.evaluate("x + 5")}"
15
18
 
16
19
  results in an output of
17
20
 
18
21
  65.0
22
+ 70.0
19
23
 
20
24
  if you missed a closing parenthesis, had an operator where it wasn't meant to be, you might get something like this:
21
25
 
22
- Unexpected multiplication(*), expected: number or open_parenthesis
26
+ Unexpected multiplication(*), expected: number, variable name or open_parenthesis
23
27
 
24
28
  and that is pretty much every feature so far. Please let me know of any bugs or additions that you'd like to see!
data/lib/math_engine.rb CHANGED
@@ -5,7 +5,34 @@ require File.expand_path(File.join(File.dirname(__FILE__), 'math_lexer'))
5
5
  require File.expand_path(File.join(File.dirname(__FILE__), 'math_parser'))
6
6
 
7
7
  class MathEngine
8
+ def initialize
9
+ @variables = {}
10
+ end
11
+
8
12
  def evaluate(expression)
9
- MathParser.new(MathLexer.new(expression)).parse.evaluate
13
+ MathParser.new(MathLexer.new(expression)).parse.evaluate(self)
14
+ end
15
+
16
+ def set(variable_name, value)
17
+ @variables[variable_name] = value
18
+ end
19
+
20
+ def get(variable_name)
21
+ raise UnknownVariableError.new(variable_name) unless @variables.keys.include? variable_name
22
+ @variables[variable_name]
23
+ end
24
+
25
+ def variables
26
+ @variables.keys
27
+ end
28
+
29
+ class UnknownVariableError < StandardError
30
+ def initialize(variable_name)
31
+ @variable_name = variable_name
32
+ end
33
+
34
+ def to_s
35
+ "Variable '#{@variable_name}' was referenced but does not exist"
36
+ end
10
37
  end
11
38
  end
data/lib/math_lexer.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  MathLexer = Lexr.that {
2
2
  ignores /\s/ => :whitespace
3
+ matches /[a-z][a-z0-9]*/ => :identifier, :convert_with => lambda { |v| v.to_sym }
3
4
  matches /[-+]?[0-9]*\.?[0-9]+/ => :number, :convert_with => lambda { |v| Float(v) }
5
+ matches '=' => :assignment
4
6
  matches '+' => :addition
5
7
  matches '-' => :subtraction
6
8
  matches '*' => :multiplication
data/lib/math_parser.rb CHANGED
@@ -4,10 +4,10 @@ class MathParser
4
4
  end
5
5
 
6
6
  def parse
7
- #statement = expression <end>
7
+ #statement = { <identifier> <assignment> } expression <end>
8
8
  #expression = term { ( <addition> | <subtraction> ) term }
9
9
  #term = factor { ( <multiplication> | <division> ) factor }
10
- #factor = <number> | <open_parenthesis> expression <close_parenthesis>
10
+ #factor = <identifier> | <number> | <open_parenthesis> expression <close_parenthesis>
11
11
  statement
12
12
  end
13
13
 
@@ -15,7 +15,15 @@ class MathParser
15
15
 
16
16
  def statement
17
17
  next!
18
- result = expression
18
+ if current.type == :identifier && peek.type == :assignment
19
+ variable_name = current.value
20
+ next!
21
+ expect_current :assignment
22
+ next!
23
+ result = AssignmentNode.new(IdentifierNode.new(variable_name), expression)
24
+ else
25
+ result = expression
26
+ end
19
27
  next!
20
28
  expect_current :end
21
29
  result
@@ -44,12 +52,14 @@ class MathParser
44
52
  end
45
53
 
46
54
  def factor
47
- if current.type == :number
48
- result = LiteralNumberNode.new(current.value)
49
- next!
50
- return result
51
- end
52
- expect_current :open_parenthesis, "number or open_parenthesis"
55
+ if [:number, :identifier].include? current.type
56
+ node_type = current.type == :number ? LiteralNumberNode : IdentifierNode
57
+ result = node_type.new(current.value)
58
+ next!
59
+ return result
60
+ end
61
+
62
+ expect_current :open_parenthesis, "number, variable or open_parenthesis"
53
63
  next!
54
64
  result = expression
55
65
  expect_current :close_parenthesis
@@ -62,6 +72,10 @@ class MathParser
62
72
  def current
63
73
  @lexer.current
64
74
  end
75
+
76
+ def peek
77
+ @lexer.peek
78
+ end
65
79
 
66
80
  def next!
67
81
  @lexer.next
@@ -79,42 +93,56 @@ class MathParser
79
93
  @left, @right = left, right
80
94
  end
81
95
 
82
- def evaluate
96
+ def evaluate(engine)
83
97
  raise "Evaluate not overridden in #{self.class.name}"
84
98
  end
85
99
  end
86
100
 
87
101
  class LiteralNumberNode < Node
88
- def evaluate
102
+ def evaluate(engine)
89
103
  value
90
104
  end
91
105
  end
92
106
 
93
107
  class ExpressionNode < Node
94
- def evaluate
95
- left.evaluate
108
+ def evaluate(engine)
109
+ left.evaluate(engine)
110
+ end
111
+ end
112
+
113
+ class IdentifierNode < Node
114
+ def evaluate(engine)
115
+ engine.get value
116
+ end
117
+ end
118
+
119
+ class AssignmentNode < Node
120
+ def evaluate(engine)
121
+ result = right.evaluate(engine)
122
+ engine.set(left.value, result)
123
+ result
96
124
  end
97
125
  end
98
126
 
99
127
  class AdditionNode < Node
100
- def evaluate
101
- left.evaluate + right.evaluate
128
+ def evaluate(engine)
129
+ left.evaluate(engine) + right.evaluate(engine)
102
130
  end
103
131
  end
104
132
 
105
133
  class SubtractionNode < Node
106
- def evaluate
107
- left.evaluate - right.evaluate
134
+ def evaluate(engine)
135
+ left.evaluate(engine) - right.evaluate(engine)
108
136
  end
109
137
  end
110
138
  class MultiplicationNode < Node
111
- def evaluate
112
- left.evaluate * right.evaluate
139
+ def evaluate(engine)
140
+ left.evaluate(engine) * right.evaluate(engine)
113
141
  end
114
142
  end
115
143
  class DivisionNode < Node
116
- def evaluate
117
- left.evaluate / right.evaluate
144
+ def evaluate(engine)
145
+ left.evaluate(engine) / right.evaluate(engine)
118
146
  end
119
147
  end
120
148
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: math_engine
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 29
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 2
10
- version: 0.1.2
9
+ - 3
10
+ version: 0.1.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Michael Baldry
@@ -26,12 +26,12 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- hash: 23
29
+ hash: 19
30
30
  segments:
31
31
  - 0
32
32
  - 2
33
- - 0
34
- version: 0.2.0
33
+ - 2
34
+ version: 0.2.2
35
35
  type: :runtime
36
36
  version_requirements: *id001
37
37
  - !ruby/object:Gem::Dependency