lxl 0.1.0 → 0.1.1
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/CHANGES +9 -0
- data/VERSION +1 -1
- data/lib/lxl.rb +37 -27
- data/test/lxl_test.rb +2 -1
- metadata +2 -1
data/CHANGES
ADDED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.1
|
data/lib/lxl.rb
CHANGED
@@ -43,17 +43,19 @@
|
|
43
43
|
#
|
44
44
|
# * The number zero is interpereted as FALSE
|
45
45
|
# * Return multiple results in an array by separating formulas with a semi-colon (;)
|
46
|
+
# * A :SYMBOL token is recognized as a ruby symbol
|
46
47
|
#
|
47
48
|
# =Lexical Types
|
48
49
|
#
|
49
50
|
# w Whitespace (includes Commas)
|
50
51
|
# ; Semi-Colon (statement separator)
|
51
52
|
# o Operator
|
52
|
-
# i Integer
|
53
53
|
# f Float
|
54
|
+
# i Integer
|
55
|
+
# ' String (single quoted)
|
56
|
+
# " String (double quoted)
|
57
|
+
# s Symbol
|
54
58
|
# t Token
|
55
|
-
# s String (single quoted)
|
56
|
-
# S String (double quoted)
|
57
59
|
# ( Open (
|
58
60
|
# ) Close )
|
59
61
|
# C Constant
|
@@ -89,26 +91,25 @@ class LXL::Parser
|
|
89
91
|
def initialize
|
90
92
|
|
91
93
|
# Constants
|
92
|
-
@constants =
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
}
|
94
|
+
@constants = Hash.new
|
95
|
+
register_constant(:TRUE, true)
|
96
|
+
register_constant(:FALSE, false)
|
97
|
+
register_constant(:NULL, nil)
|
97
98
|
|
98
99
|
# Functions
|
99
100
|
@functions = Hash.new
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
101
|
+
register_function(:AND) { |a,b| to_b(a) && to_b(b) }
|
102
|
+
register_function(:OR) { |a,b| to_b(a) || to_b(b) }
|
103
|
+
register_function(:IF) { |v,a,b| to_b(v) ? a : b }
|
104
|
+
register_function(:LIST) { |*args| args }
|
105
|
+
register_function(:IN) { |n,h| h.respond_to?(:include?) ? h.include?(n) : false }
|
106
|
+
register_function(:TODAY) { Date.today.ajd.to_f }
|
107
|
+
register_function(:NOW) { DateTime.now.ajd.to_f }
|
108
|
+
register_function(:DATE) { |y,m,d| Date.new(y,m,d).ajd.to_f }
|
109
|
+
register_function(:DATETIME) { |value| DateTime.parse(value).ajd.to_f }
|
110
|
+
register_function(:TIME) { |h,m,s|
|
109
111
|
DateTime.valid_time?(h,m,s) ? DateTime.valid_time?(h,m,s).to_f : raise(ArgumentError, 'invalid time')
|
110
112
|
}
|
111
|
-
register(:DATETIME) { |value| DateTime.parse(value).ajd.to_f }
|
112
113
|
|
113
114
|
# Lexer
|
114
115
|
ops = EXCEL_OPERATORS.collect { |v| Regexp.quote(v) }.join('|')
|
@@ -119,8 +120,9 @@ class LXL::Parser
|
|
119
120
|
[/\A(#{ops})/,?o], # Operator
|
120
121
|
[/\A[0-9]+\.[0-9]+/,?f], # Float
|
121
122
|
[/\A[0-9]+/,?i], # Integer
|
122
|
-
[/\A("[^\"]*")/m,?
|
123
|
-
[/\A('[^\']*')/m,?
|
123
|
+
[/\A("[^\"]*")/m,?'], # String (single quoted)
|
124
|
+
[/\A('[^\']*')/m,?"], # String (double quoted)
|
125
|
+
[/\A:\w+/,?s], # Symbol
|
124
126
|
[/\A\w+/,?t], # Token
|
125
127
|
[/\A\(/,?(], # Open (
|
126
128
|
[/\A\)/,?)], # Close )
|
@@ -153,12 +155,20 @@ class LXL::Parser
|
|
153
155
|
|
154
156
|
protected
|
155
157
|
|
158
|
+
# Register a constant
|
159
|
+
#
|
160
|
+
# * Converts name to symbol
|
161
|
+
#
|
162
|
+
def register_constant(name, value)
|
163
|
+
@constants[name.to_sym] = value
|
164
|
+
end
|
165
|
+
|
156
166
|
# Register a function
|
157
167
|
#
|
158
168
|
# * Converts name to symbol
|
159
169
|
# * Wraps function with a debugging procedure
|
160
170
|
#
|
161
|
-
def
|
171
|
+
def register_function(name, &block)
|
162
172
|
@functions[name.to_sym] = debug(name.to_sym, &block)
|
163
173
|
end
|
164
174
|
|
@@ -166,13 +176,13 @@ class LXL::Parser
|
|
166
176
|
#
|
167
177
|
# * Raise an error unless given the correct number of arguments
|
168
178
|
#
|
169
|
-
def debug(name, &
|
179
|
+
def debug(name, &block)
|
170
180
|
raise ArgumentError, 'block not given to debug' unless block_given?
|
171
181
|
Proc.new { |*args|
|
172
|
-
if
|
173
|
-
raise ArgumentError, "wrong number of arguments (#{args.size} for #{
|
182
|
+
if block.arity >= 0 and block.arity != args.size
|
183
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for #{block.arity}) for #{name}"
|
174
184
|
end
|
175
|
-
|
185
|
+
block.call(*args)
|
176
186
|
}
|
177
187
|
end
|
178
188
|
|
@@ -185,12 +195,13 @@ class LXL::Parser
|
|
185
195
|
types, @tokens = @lexer.scan(formula)
|
186
196
|
@types = types.split(//)
|
187
197
|
raise SyntaxError, 'unbalanced parentheses' unless balanced?
|
188
|
-
|
198
|
+
|
189
199
|
# Parse tokens
|
190
200
|
@tokens.each_index do |i|
|
191
201
|
type, token = @types[i], @tokens[i]
|
192
202
|
token = token.to_i if type == 'i'
|
193
203
|
token = token.to_f if type == 'f'
|
204
|
+
token = token.to_sym if type == 's'
|
194
205
|
if type == 't'
|
195
206
|
token = token.to_sym
|
196
207
|
if @functions.key?(token)
|
@@ -300,7 +311,6 @@ if $0 == __FILE__
|
|
300
311
|
IF(1+1=2, "yes", "no");
|
301
312
|
}
|
302
313
|
|
303
|
-
# single formula
|
304
314
|
puts LXL.eval('5+5').inspect # => 10
|
305
315
|
|
306
316
|
# multiple formulas separated by semi-colon
|
data/test/lxl_test.rb
CHANGED
@@ -10,6 +10,7 @@ class LXLTest < Test::Unit::TestCase
|
|
10
10
|
|
11
11
|
def test_multiple_formula
|
12
12
|
formulas = %{
|
13
|
+
:SYMBOL;
|
13
14
|
((1+2)*(10-6))/2;
|
14
15
|
DATETIME("2004-11-22 11:11:00")=DATE(2004,11,22)+TIME(11,11,00);
|
15
16
|
IN(" is ", "this is a string");
|
@@ -19,7 +20,7 @@ class LXLTest < Test::Unit::TestCase
|
|
19
20
|
OR(TRUE, FALSE);
|
20
21
|
IF(1+1=2, "yes", "no");
|
21
22
|
}
|
22
|
-
expected = [6, true, true, [1, "two", 3.0], true, false, true, "yes"]
|
23
|
+
expected = [:SYMBOL, 6, true, true, [1, "two", 3.0], true, false, true, "yes"]
|
23
24
|
results = LXL.eval(formulas)
|
24
25
|
expected.each_index { |i| assert_equal(expected[i], results[i]) }
|
25
26
|
end
|
metadata
CHANGED
@@ -3,7 +3,7 @@ rubygems_version: 0.8.3
|
|
3
3
|
specification_version: 1
|
4
4
|
name: lxl
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.1.
|
6
|
+
version: 0.1.1
|
7
7
|
date: 2005-02-08
|
8
8
|
summary: LXL (Like Excel) is a mini-language that mimics Microsoft Excel formulas. Easily extended with new constants and functions.
|
9
9
|
require_paths:
|
@@ -29,6 +29,7 @@ authors:
|
|
29
29
|
files:
|
30
30
|
- lib
|
31
31
|
- test
|
32
|
+
- CHANGES
|
32
33
|
- README
|
33
34
|
- VERSION
|
34
35
|
- lib/lxl.rb
|