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.
Files changed (5) hide show
  1. data/CHANGES +9 -0
  2. data/VERSION +1 -1
  3. data/lib/lxl.rb +37 -27
  4. data/test/lxl_test.rb +2 -1
  5. metadata +2 -1
data/CHANGES ADDED
@@ -0,0 +1,9 @@
1
+ 0.1.1
2
+
3
+ - String tokens s/S => '/"
4
+ - :SYMBOL recognition (s token)
5
+ - register_constant, register_function
6
+
7
+ 0.1.0
8
+
9
+ - Initial release
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
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
- :TRUE => true,
94
- :FALSE => false,
95
- :NULL => nil,
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
- register(:AND) { |a,b| to_b(a) && to_b(b) }
101
- register(:OR) { |a,b| to_b(a) || to_b(b) }
102
- register(:IF) { |v,a,b| to_b(v) ? a : b }
103
- register(:LIST) { |*args| args }
104
- register(:IN) { |n,h| h.respond_to?(:include?) ? h.include?(n) : false }
105
- register(:TODAY) { Date.today.ajd.to_f }
106
- register(:NOW) { DateTime.now.ajd.to_f }
107
- register(:DATE) { |y,m,d| Date.new(y,m,d).ajd.to_f }
108
- register(:TIME) { |h,m,s|
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,?s], # String (single quoted)
123
- [/\A('[^\']*')/m,?S], # String (double quoted)
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 register(name, &block)
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, &func)
179
+ def debug(name, &block)
170
180
  raise ArgumentError, 'block not given to debug' unless block_given?
171
181
  Proc.new { |*args|
172
- if func.arity >= 0 and func.arity != args.size
173
- raise ArgumentError, "wrong number of arguments (#{args.size} for #{func.arity}) for #{name}"
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
- func.call(*args)
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.0
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