minad-evaluator 0.1.3 → 0.1.5

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.
@@ -12,8 +12,24 @@ Usage
12
12
 
13
13
  See the test cases for more examples.
14
14
 
15
+ Calculator
16
+ ----------
17
+
18
+ A small calculator program (calc.rb) is provided with this library. You can use
19
+ it as follows:
20
+
21
+ $ ./calc.rb
22
+ > number := 10
23
+ 10
24
+ > number * 3
25
+ 30
26
+ > 1 [joule] in [MeV]
27
+ 6241509647120.42 MeV
28
+
29
+ The calculator loads a few natural constants at startup (calc.startup). For unit support
30
+ my minad-units library is used. Units are denoted in brackets e.g. [meter], [kV] etc
31
+
15
32
  Authors
16
33
  -------
17
34
 
18
35
  Daniel Mendler
19
-
data/Rakefile CHANGED
@@ -3,7 +3,7 @@ require 'hoe'
3
3
  $:.unshift 'lib'
4
4
  require 'evaluator'
5
5
 
6
- Hoe.new 'evaluator', Evaluator::VERSION do |latex|
7
- latex.developer 'Daniel Mendler', 'mail@daniel-mendler.de'
6
+ Hoe.new 'evaluator', Evaluator::VERSION do |evaluator|
7
+ evaluator.developer 'Daniel Mendler', 'mail@daniel-mendler.de'
8
8
  end
9
9
 
@@ -1,31 +1,39 @@
1
1
  require 'complex'
2
+ begin
3
+ require 'units'
4
+ rescue LoadError
5
+ end
2
6
 
3
7
  module Evaluator
4
8
  def self.infix(priority, unary = nil, &block) [false, priority, lambda(&block), unary] end
5
9
  def self.prefix(&block) [true, 1e5, lambda(&block)] end
6
10
 
7
- VERSION = "0.1.3"
11
+ VERSION = '0.1.5'
8
12
  OPERATOR = {
9
- '||' => infix(0) {|a,b| a || b },
10
- '&&' => infix(1) {|a,b| a && b },
11
- '==' => infix(2) {|a,b| a == b },
12
- '!=' => infix(2) {|a,b| a != b },
13
- '<=' => infix(2) {|a,b| a <= b },
14
- '>=' => infix(2) {|a,b| a >= b },
15
- '<' => infix(2) {|a,b| a < b },
16
- '>' => infix(2) {|a,b| a > b },
17
- '+' => infix(3, 'plus') {|a,b| a + b },
18
- '-' => infix(3, 'minus') {|a,b| a - b },
19
- '>>' => infix(4) {|a,b| a >> b },
20
- '<<' => infix(4) {|a,b| a << b },
21
- '&' => infix(5) {|a,b| a & b },
22
- '|' => infix(5) {|a,b| a | b },
23
- '^' => infix(5) {|a,b| a ^ b },
24
- '*' => infix(6) {|a,b| a * b },
25
- '/' => infix(6) {|a,b| a / b },
26
- '%' => infix(6) {|a,b| a % b },
27
- 'div' => infix(6) {|a,b| a.div b },
28
- '**' => infix(7) {|a,b| a ** b },
13
+ 'in' => infix(0) do |a,b|
14
+ raise(RuntimeError, 'Unit support not available') if !a.respond_to? :in
15
+ a.in(b)
16
+ end,
17
+ '||' => infix(1) {|a,b| a || b },
18
+ '&&' => infix(2) {|a,b| a && b },
19
+ '==' => infix(3) {|a,b| a == b },
20
+ '!=' => infix(3) {|a,b| a != b },
21
+ '<=' => infix(3) {|a,b| a <= b },
22
+ '>=' => infix(3) {|a,b| a >= b },
23
+ '<' => infix(3) {|a,b| a < b },
24
+ '>' => infix(3) {|a,b| a > b },
25
+ '+' => infix(4, 'plus') {|a,b| a + b },
26
+ '-' => infix(4, 'minus') {|a,b| a - b },
27
+ '>>' => infix(5) {|a,b| a >> b },
28
+ '<<' => infix(5) {|a,b| a << b },
29
+ '&' => infix(6) {|a,b| a & b },
30
+ '|' => infix(6) {|a,b| a | b },
31
+ '^' => infix(6) {|a,b| a ^ b },
32
+ '*' => infix(7) {|a,b| a * b },
33
+ '/' => infix(7) {|a,b| a / b },
34
+ '%' => infix(7) {|a,b| a % b },
35
+ 'div' => infix(7) {|a,b| a.div b },
36
+ '**' => infix(8) {|a,b| a ** b },
29
37
  'gcd' => prefix {|x,y| x.gcd(y) },
30
38
  'lcm' => prefix {|x,y| x.lcm(y) },
31
39
  'sin' => prefix {|x| Math.sin(x) },
@@ -69,6 +77,7 @@ module Evaluator
69
77
  'reverse' => prefix {|x| x.reverse },
70
78
  'index' => prefix {|x,y| x.index(y) },
71
79
  'rindex' => prefix {|x,y| x.rindex(y) },
80
+ '=' => '==',
72
81
  'or' => '||',
73
82
  'and' => '&&',
74
83
  'mod' => '%',
@@ -89,7 +98,7 @@ module Evaluator
89
98
  'nil' => nil,
90
99
  'e' => Math::E,
91
100
  'pi' => Math::PI,
92
- 'i' => Complex::I
101
+ 'i' => Complex::I,
93
102
  }
94
103
  STRING = /^(?:'(?:\\'|[^'])*'|"(?:\\"|[^"])*")$/
95
104
  REAL = /^(?:(?:\d*\.\d+|\d+\.\d*)(?:[eE][-+]?\d+)?|\d+[eE][-+]?\d+)$/
@@ -97,48 +106,64 @@ module Evaluator
97
106
  OCT = /^0[0-7]+$/
98
107
  DEC = /^\d+$/
99
108
  SYMBOL = /^[a-zA-Z_][\w_]*$/
100
- VALUE_TOKENS = [STRING, REAL, HEX, OCT, DEC, SYMBOL].map {|x| x.source[1..-2] }
109
+ UNIT = /^\[[^\]]+\]$/
110
+ VALUE_TOKENS = [UNIT, STRING, REAL, HEX, OCT, DEC, SYMBOL].map {|x| x.source[1..-2] }
101
111
  OPERATOR_TOKENS = OPERATOR.keys.flatten.sort { |a,b| b.length <=> a.length}.map { |x| Regexp.quote(x) }
102
112
  TOKENIZER = Regexp.new((VALUE_TOKENS + OPERATOR_TOKENS + ['\\(', '\\)', ',']).join('|'))
103
113
 
104
114
  def self.eval(expr, vars = {})
105
- vars = Hash[*vars.map {|k,v| [k.to_s.downcase, v] }.flatten].merge(CONSTANTS)
115
+ vars = Hash[*vars.merge(CONSTANTS).map {|k,v| [k.to_s.downcase, v] }.flatten]
106
116
  stack, result, unary = [], [], true
107
117
  expr.to_s.scan(TOKENIZER).each do |tok|
108
118
  if tok == '('
109
119
  stack << '('
120
+ unary = true
110
121
  elsif tok == ')'
111
122
  exec(result, stack.pop) while !stack.empty? && stack.last != '('
112
- raise(SyntaxError, "Unexpected token )") if stack.empty?
123
+ raise(SyntaxError, 'Unexpected token )') if stack.empty?
113
124
  stack.pop
125
+ unary = false
114
126
  elsif tok == ','
115
127
  exec(result, stack.pop) while !stack.empty? && stack.last != '('
128
+ unary = true
116
129
  elsif operator = OPERATOR[tok.downcase]
130
+ # Check for alias
117
131
  tok = String === operator ? operator : tok.downcase
132
+ operator = OPERATOR[tok]
118
133
  if operator[0]
134
+ # Prefix operator
119
135
  stack << tok
120
136
  elsif unary && operator[3]
137
+ # Alternative prefix operator
121
138
  stack << operator[3]
122
139
  else
140
+ # Infix operator
123
141
  exec(result, stack.pop) while !stack.empty? && stack.last != '(' && OPERATOR[stack.last][1] >= operator[1]
124
142
  stack << tok
125
143
  end
144
+ unary = true
126
145
  else
127
- result << case tok
128
- when STRING then tok[1..-2].gsub(/\\"/, '"').gsub(/\\'/, "'")
129
- when REAL then tok.to_f
130
- when HEX then tok.to_i(16)
131
- when OCT then tok.to_i(8)
132
- when DEC then tok.to_i(10)
133
- when SYMBOL
134
- tok.downcase!
135
- raise(NameError, "Symbol #{tok} is undefined") if !vars.include?(tok)
136
- vars[tok]
137
- end
146
+ val = case tok
147
+ when UNIT
148
+ if tok.respond_to? :to_unit
149
+ tok[1..-2].to_unit
150
+ else
151
+ raise(RuntimeError, 'Unit support not available')
152
+ end
153
+ when STRING then tok[1..-2].gsub(/\\"/, '"').gsub(/\\'/, "'")
154
+ when REAL then tok.to_f
155
+ when HEX then tok.to_i(16)
156
+ when OCT then tok.to_i(8)
157
+ when DEC then tok.to_i(10)
158
+ when SYMBOL
159
+ tok.downcase!
160
+ raise(NameError, "Symbol #{tok} is undefined") if !vars.include?(tok)
161
+ vars[tok]
162
+ end
163
+ stack << '*' if !unary
164
+ result << val
138
165
  unary = false
139
- next
140
166
  end
141
- unary = true
142
167
  end
143
168
  exec(result, stack.pop) while !stack.empty?
144
169
  result.last
@@ -146,9 +171,9 @@ module Evaluator
146
171
 
147
172
  def self.exec(result, op)
148
173
  raise(SyntaxError, "Unexpected token #{op}") if !OPERATOR.include?(op)
149
- op = OPERATOR[op][2]
150
- raise(SyntaxError, "Not enough operands for #{op}") if result.size < op.arity
151
- result << op[*result.slice!(-op.arity, op.arity)]
174
+ fn = OPERATOR[op][2]
175
+ raise(SyntaxError, "Not enough operands for #{op}") if result.size < fn.arity
176
+ result << fn[*result.slice!(-fn.arity, fn.arity)]
152
177
  end
153
178
 
154
179
  private_class_method :infix, :prefix, :exec
@@ -30,6 +30,8 @@ class TestEvaluator < Test::Unit::TestCase
30
30
 
31
31
  assert_equal 3, Evaluator('1+2')
32
32
  assert_equal 'xyz', Evaluator('"x"+"yz"')
33
+ assert_equal 'a1b', Evaluator("'a'+string 1+'b'")
34
+ assert_equal 'a1b', Evaluator("'a'+string(1)+'b'")
33
35
 
34
36
  assert_equal 3, Evaluator('5-2')
35
37
  assert_equal(-1, Evaluator('3-4'))
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minad-evaluator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Mendler