math_engine 0.1.2 → 0.1.3

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