minad-evaluator 0.1.1 → 0.1.2
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/lib/evaluator.rb +103 -80
- data/test/test_evaluator.rb +27 -1
- metadata +1 -1
data/lib/evaluator.rb
CHANGED
|
@@ -4,64 +4,84 @@ module Evaluator
|
|
|
4
4
|
def self.infix(priority, unary = nil, &block) [false, priority, lambda(&block), unary] end
|
|
5
5
|
def self.prefix(&block) [true, 1e5, lambda(&block)] end
|
|
6
6
|
|
|
7
|
-
VERSION = "0.1.
|
|
7
|
+
VERSION = "0.1.2"
|
|
8
8
|
OPERATOR = {
|
|
9
|
-
'||'
|
|
10
|
-
'
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
'
|
|
18
|
-
'
|
|
19
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'
|
|
26
|
-
'
|
|
27
|
-
'
|
|
28
|
-
'
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
-
'
|
|
32
|
-
'
|
|
33
|
-
'
|
|
34
|
-
'
|
|
35
|
-
'
|
|
36
|
-
'
|
|
37
|
-
'
|
|
38
|
-
'
|
|
39
|
-
'
|
|
40
|
-
'
|
|
41
|
-
'
|
|
42
|
-
'
|
|
43
|
-
'
|
|
44
|
-
'
|
|
45
|
-
'
|
|
46
|
-
'
|
|
47
|
-
'
|
|
48
|
-
'
|
|
49
|
-
'floor'
|
|
50
|
-
'ceil'
|
|
51
|
-
'string'
|
|
52
|
-
'int'
|
|
53
|
-
'float'
|
|
54
|
-
'rand'
|
|
55
|
-
'conj'
|
|
56
|
-
'im'
|
|
57
|
-
're'
|
|
58
|
-
'round'
|
|
59
|
-
'abs'
|
|
60
|
-
'minus'
|
|
61
|
-
'plus'
|
|
62
|
-
'!'
|
|
63
|
-
'
|
|
64
|
-
'
|
|
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 },
|
|
29
|
+
'gcd' => prefix {|x,y| x.gcd(y) },
|
|
30
|
+
'lcm' => prefix {|x,y| x.lcm(y) },
|
|
31
|
+
'sin' => prefix {|x| Math.sin(x) },
|
|
32
|
+
'cos' => prefix {|x| Math.cos(x) },
|
|
33
|
+
'tan' => prefix {|x| Math.tan(x) },
|
|
34
|
+
'sinh' => prefix {|x| Math.sinh(x) },
|
|
35
|
+
'cosh' => prefix {|x| Math.cosh(x) },
|
|
36
|
+
'tanh' => prefix {|x| Math.tanh(x) },
|
|
37
|
+
'asin' => prefix {|x| Math.asin(x) },
|
|
38
|
+
'acos' => prefix {|x| Math.acos(x) },
|
|
39
|
+
'atan' => prefix {|x| Math.atan(x) },
|
|
40
|
+
'asinh' => prefix {|x| Math.asinh(x) },
|
|
41
|
+
'atanh' => prefix {|x| Math.atanh(x) },
|
|
42
|
+
'sqrt' => prefix {|x| Math.sqrt(x) },
|
|
43
|
+
'log' => prefix {|x| Math.log(x) },
|
|
44
|
+
'log10' => prefix {|x| Math.log10(x) },
|
|
45
|
+
'log2' => prefix {|x| Math.log(x)/Math.log(2) },
|
|
46
|
+
'exp' => prefix {|x| Math.exp(x) },
|
|
47
|
+
'erf' => prefix {|x| Math.erf(x) },
|
|
48
|
+
'erfc' => prefix {|x| Math.erfc(x) },
|
|
49
|
+
'floor' => prefix {|x| x.floor },
|
|
50
|
+
'ceil' => prefix {|x| x.ceil },
|
|
51
|
+
'string' => prefix {|x| x.to_s },
|
|
52
|
+
'int' => prefix {|x| x.to_i },
|
|
53
|
+
'float' => prefix {|x| x.to_f },
|
|
54
|
+
'rand' => prefix {|| rand },
|
|
55
|
+
'conj' => prefix {|x| x.conj },
|
|
56
|
+
'im' => prefix {|x| x.imag },
|
|
57
|
+
're' => prefix {|x| x.real },
|
|
58
|
+
'round' => prefix {|x| x.round },
|
|
59
|
+
'abs' => prefix {|x| x.abs },
|
|
60
|
+
'minus' => prefix {|x| -x },
|
|
61
|
+
'plus' => prefix {|x| x },
|
|
62
|
+
'!' => prefix {|x| !x },
|
|
63
|
+
'~' => prefix {|x| ~x },
|
|
64
|
+
'substr' => prefix {|x,a,b| x.slice(a,b) },
|
|
65
|
+
'len' => prefix {|x| x.length },
|
|
66
|
+
'tolower' => prefix {|x| x.downcase },
|
|
67
|
+
'toupper' => prefix {|x| x.upcase },
|
|
68
|
+
'strip' => prefix {|x| x.strip },
|
|
69
|
+
'reverse' => prefix {|x| x.reverse },
|
|
70
|
+
'index' => prefix {|x,y| x.index(y) },
|
|
71
|
+
'rindex' => prefix {|x,y| x.rindex(y) },
|
|
72
|
+
'or' => '||',
|
|
73
|
+
'and' => '&&',
|
|
74
|
+
'mod' => '%',
|
|
75
|
+
'ln' => 'log',
|
|
76
|
+
'imag' => 'im',
|
|
77
|
+
'real' => 're',
|
|
78
|
+
'count' => 'len',
|
|
79
|
+
'size' => 'len',
|
|
80
|
+
'length' => 'len',
|
|
81
|
+
'trim' => 'strip',
|
|
82
|
+
'downcase' => 'tolower',
|
|
83
|
+
'upcase' => 'toupper',
|
|
84
|
+
'slice' => 'substr',
|
|
65
85
|
}
|
|
66
86
|
CONSTANTS = {
|
|
67
87
|
'true' => true,
|
|
@@ -71,22 +91,15 @@ module Evaluator
|
|
|
71
91
|
'pi' => Math::PI,
|
|
72
92
|
'i' => Complex::I
|
|
73
93
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
vars[tok]
|
|
84
|
-
}
|
|
85
|
-
]
|
|
86
|
-
]
|
|
87
|
-
TOKENIZER = Regexp.new((TOKENS.map {|x| x[0].source[1..-2] } +
|
|
88
|
-
OPERATOR.keys.flatten.sort { |a,b| b.length <=> a.length}.map { |op| Regexp.quote(op) }).
|
|
89
|
-
join('|') + '|\\(|\\)|,')
|
|
94
|
+
STRING = /^(?:'(?:\\'|[^'])*'|"(?:\\"|[^"])*")$/
|
|
95
|
+
REAL = /^(?:(?:\d*\.\d+|\d+\.\d*)(?:[eE][-+]?\d+)?|\d+[eE][-+]?\d+)$/
|
|
96
|
+
HEX = /^0[xX][\dA-Fa-f]+$/
|
|
97
|
+
OCT = /^0[0-7]+$/
|
|
98
|
+
DEC = /^\d+$/
|
|
99
|
+
SYMBOL = /^[a-zA-Z_][\w_]*$/
|
|
100
|
+
VALUE_TOKENS = [STRING, REAL, HEX, OCT, DEC, SYMBOL].map {|x| x.source[1..-2] }
|
|
101
|
+
OPERATOR_TOKENS = OPERATOR.keys.flatten.sort { |a,b| b.length <=> a.length}.map { |x| Regexp.quote(x) }
|
|
102
|
+
TOKENIZER = Regexp.new((VALUE_TOKENS + OPERATOR_TOKENS + ['\\(', '\\)', ',']).join('|'))
|
|
90
103
|
|
|
91
104
|
def self.eval(expr, vars = {})
|
|
92
105
|
vars = Hash[*vars.map {|k,v| [k.to_s.downcase, v] }.flatten].merge(CONSTANTS)
|
|
@@ -100,18 +113,28 @@ module Evaluator
|
|
|
100
113
|
stack.pop
|
|
101
114
|
elsif tok == ','
|
|
102
115
|
exec(result, stack.pop) while !stack.empty? && stack.last != '('
|
|
103
|
-
elsif OPERATOR
|
|
104
|
-
tok.downcase
|
|
105
|
-
if
|
|
116
|
+
elsif operator = OPERATOR[tok.downcase]
|
|
117
|
+
tok = String === operator ? operator : tok.downcase
|
|
118
|
+
if operator[0]
|
|
106
119
|
stack << tok
|
|
107
|
-
elsif unary &&
|
|
108
|
-
stack <<
|
|
120
|
+
elsif unary && operator[3]
|
|
121
|
+
stack << operator[3]
|
|
109
122
|
else
|
|
110
|
-
exec(result, stack.pop) while !stack.empty? && stack.last != '(' && OPERATOR[stack.last][1] >=
|
|
123
|
+
exec(result, stack.pop) while !stack.empty? && stack.last != '(' && OPERATOR[stack.last][1] >= operator[1]
|
|
111
124
|
stack << tok
|
|
112
125
|
end
|
|
113
126
|
else
|
|
114
|
-
result <<
|
|
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
|
|
115
138
|
unary = false
|
|
116
139
|
next
|
|
117
140
|
end
|
data/test/test_evaluator.rb
CHANGED
|
@@ -78,7 +78,10 @@ class TestEvaluator < Test::Unit::TestCase
|
|
|
78
78
|
assert_equal nil, Evaluator('niL')
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
-
def
|
|
81
|
+
def test_numeric_functions
|
|
82
|
+
assert_equal 13, Evaluator('gcd(26, 39)')
|
|
83
|
+
assert_equal 78, Evaluator('lcm(26, 39)')
|
|
84
|
+
assert_equal Math.cos(42), Evaluator('cos 42')
|
|
82
85
|
assert_equal Math.sin(42), Evaluator('sin 42')
|
|
83
86
|
assert_equal Math.cos(42), Evaluator('cos 42')
|
|
84
87
|
assert_equal Math.tan(42), Evaluator('tan 42')
|
|
@@ -96,6 +99,8 @@ class TestEvaluator < Test::Unit::TestCase
|
|
|
96
99
|
assert_equal Math.log10(42) + 3, Evaluator('log10 42 + 3')
|
|
97
100
|
assert_equal Math.log(42)/Math.log(2) + 3, Evaluator('log2 42 + 3')
|
|
98
101
|
assert_equal 3 * Math.exp(42), Evaluator('3 * exp 42')
|
|
102
|
+
assert_equal Math.erf(2), Evaluator('erf 2')
|
|
103
|
+
assert_equal Math.erfc(2), Evaluator('erfc 2')
|
|
99
104
|
assert_equal 42, Evaluator('floor 42.3')
|
|
100
105
|
assert_equal 42, Evaluator('ceil 41.6')
|
|
101
106
|
assert_equal 3.5, Evaluator('float("3.5")')
|
|
@@ -113,8 +118,16 @@ class TestEvaluator < Test::Unit::TestCase
|
|
|
113
118
|
assert_equal 3, Evaluator('plus 3')
|
|
114
119
|
assert_equal(-3, Evaluator('minus 3'))
|
|
115
120
|
assert_equal false, Evaluator('!3')
|
|
121
|
+
assert_equal ~3, Evaluator('~3')
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def test_string_functions
|
|
116
125
|
assert_equal 'bcd', Evaluator('substr("abcde", 1, 3)')
|
|
117
126
|
assert_equal 4, Evaluator('len("abcd")')
|
|
127
|
+
assert_equal 'abc', Evaluator('strip " abc "')
|
|
128
|
+
assert_equal 'cba', Evaluator('reverse "abc"')
|
|
129
|
+
assert_equal 2, Evaluator('index("abcdefg", "cde")')
|
|
130
|
+
assert_equal 7, Evaluator('rindex("abcdefgcdef", "cde")')
|
|
118
131
|
end
|
|
119
132
|
|
|
120
133
|
def test_variables
|
|
@@ -137,5 +150,18 @@ class TestEvaluator < Test::Unit::TestCase
|
|
|
137
150
|
assert_equal 0xABCDEF123, Evaluator('0xABCDEF123')
|
|
138
151
|
assert_equal 01234, Evaluator('01234')
|
|
139
152
|
assert_equal 234, Evaluator('234 ')
|
|
153
|
+
assert_equal 0.123, Evaluator('.123')
|
|
154
|
+
assert_equal 0.123, Evaluator('0.123')
|
|
155
|
+
assert_equal 123.0, Evaluator('123.')
|
|
156
|
+
assert_equal 123e-42, Evaluator('123e-42')
|
|
157
|
+
assert_equal 0.123e-42, Evaluator('.123e-42')
|
|
158
|
+
assert_equal 2.123e-42, Evaluator('2.123e-42')
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def test_strings
|
|
162
|
+
assert_equal "abc'a", Evaluator('"abc\'a"')
|
|
163
|
+
assert_equal 'abc"a', Evaluator('"abc\"a"')
|
|
164
|
+
assert_equal 'abc"a', Evaluator("'abc\"a'")
|
|
165
|
+
assert_equal "abc'a", Evaluator("'abc\\'a'")
|
|
140
166
|
end
|
|
141
167
|
end
|