excel_to_code 0.0.14 → 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/README.md CHANGED
@@ -39,3 +39,4 @@ There are some how to guides in the doc folder.
39
39
  3. Doesn't implement all functions (see doc/Which_functions_are_implemented.md)
40
40
  4. Doesn't implement references that involve range unions and lists
41
41
  5. Sometimes gives cells as being empty, when excel would give the cell as having a numeric value of zero
42
+ 6. The generated C version does not multithread and will give bad results if you try
@@ -58,6 +58,9 @@ class ExcelToC < ExcelToX
58
58
  close(i)
59
59
  end
60
60
 
61
+ # Need to make sure there are enough refs for named references as well
62
+ number_of_refs += named_references_to_keep.size
63
+
61
64
  o.puts "// end of definitions"
62
65
  o.puts
63
66
  o.puts "// Used to decide whether to recalculate a cell"
@@ -126,6 +129,30 @@ class ExcelToC < ExcelToX
126
129
  o.puts
127
130
  close(i)
128
131
  end
132
+
133
+ # Output the named references
134
+
135
+ # Getters
136
+ o.puts "// Start of named references"
137
+ i = input('Named references to keep')
138
+ w.rewind
139
+ c.gettable = lambda { |ref| true }
140
+ c.settable = lambda { |ref| false }
141
+ c.worksheet = ""
142
+ c.rewrite(i,w,o)
143
+ close(i)
144
+
145
+ # Setters
146
+ i = input('Named references to set')
147
+ w.rewind # Worksheet C names
148
+
149
+ c = CompileNamedReferenceSetters.new
150
+ c.cells_that_can_be_set_at_runtime = cells_that_can_be_set_at_runtime
151
+ c.rewrite(i,w,o)
152
+
153
+ close(i)
154
+ o.puts "// End of named references"
155
+
129
156
  close(w,o)
130
157
  end
131
158
 
@@ -161,6 +188,102 @@ class ExcelToC < ExcelToX
161
188
 
162
189
  code = <<END
163
190
  require 'ffi'
191
+ require 'singleton'
192
+
193
+ class #{ruby_module_name}Shim
194
+
195
+ # WARNING: this is not thread safe
196
+ def initialize
197
+ reset
198
+ end
199
+
200
+ def reset
201
+ #{ruby_module_name}.reset
202
+ end
203
+
204
+ def method_missing(name, *arguments)
205
+ if arguments.size == 0
206
+ get(name)
207
+ elsif arguments.size == 1
208
+ set(name, arguments.first)
209
+ else
210
+ super
211
+ end
212
+ end
213
+
214
+ def get(name)
215
+ return 0 unless #{ruby_module_name}.respond_to?(name)
216
+ excel_value = #{ruby_module_name}.send(name)
217
+ case excel_value[:type]
218
+ when :ExcelNumber; excel_value[:number]
219
+ when :ExcelString; excel_value[:string].read_string.force_encoding("utf-8")
220
+ when :ExcelBoolean; excel_value[:number] == 1
221
+ when :ExcelEmpty; nil
222
+ when :ExcelRange
223
+ r = excel_value[:rows]
224
+ c = excel_value[:columns]
225
+ p = excel_value[:array]
226
+ s = #{ruby_module_name}::ExcelValue.size
227
+ a = Array.new(r) { Array.new(c) }
228
+ (0...r).each do |row|
229
+ (0...c).each do |column|
230
+ a[row][column] = ruby_from_excel_value(#{ruby_module_name}::ExcelValue.new(p + (((row*c)+column)*s)))
231
+ end
232
+ end
233
+ return a
234
+ when :ExcelError; [:value,:name,:div0,:ref,:na][excel_value[:number]]
235
+ else
236
+ raise Exception.new("ExcelValue type \u0023{excel_value[:type].inspect} not recognised")
237
+ end
238
+ end
239
+
240
+ def set(name, ruby_value)
241
+ name = name.to_s
242
+ name = "set_\#{name[0..-2]}" if name.end_with?('=')
243
+ return false unless #{ruby_module_name}.respond_to?(name)
244
+ Getsetranges.send(name, excel_value_from_ruby_value(ruby_value))
245
+ end
246
+
247
+ def excel_value_from_ruby_value(ruby_value, excel_value = #{ruby_module_name}::ExcelValue.new)
248
+ case ruby_value
249
+ when Numeric
250
+ excel_value[:type] = :ExcelNumber
251
+ excel_value[:number] = ruby_value
252
+ when String
253
+ excel_value[:type] = :ExcelString
254
+ excel_value[:string] = FFI::MemoryPointer.from_string(ruby_value.encode('utf-8'))
255
+ when TrueClass, FalseClass
256
+ excel_value[:type] = :ExcelBoolean
257
+ excel_value[:number] = ruby_value ? 1 : 0
258
+ when nil
259
+ excel_value[:type] = :ExcelEmpty
260
+ when Array
261
+ excel_value[:type] = :ExcelRange
262
+ # Presumed to be a row unless specified otherwise
263
+ if ruby_value.first.is_a?(Array)
264
+ excel_value[:rows] = ruby_value.size
265
+ excel_value[:columns] = ruby_value.first.size
266
+ else
267
+ excel_value[:rows] = 1
268
+ excel_value[:columns] = ruby_value.size
269
+ end
270
+ ruby_values = ruby_value.flatten
271
+ pointer = FFI::MemoryPointer.new(#{ruby_module_name}::ExcelValue, ruby_values.size)
272
+ excel_value[:array] = pointer
273
+ ruby_values.each.with_index do |v,i|
274
+ excel_value_from_ruby_value(v, #{ruby_module_name}::ExcelValue.new(pointer[i]))
275
+ end
276
+ when Symbol
277
+ excel_value[:type] = :ExcelError
278
+ excel_value[:number] = [:value, :name, :div0, :ref, :na].index(ruby_value)
279
+ else
280
+ raise Exception.new("Ruby value \u0023{ruby_value.inspect} not translatable into excel")
281
+ end
282
+ excel_value
283
+ end
284
+
285
+ end
286
+
164
287
 
165
288
  module #{ruby_module_name}
166
289
  extend FFI::Library
@@ -170,7 +293,7 @@ module #{ruby_module_name}
170
293
  class ExcelValue < FFI::Struct
171
294
  layout :type, ExcelType,
172
295
  :number, :double,
173
- :string, :string,
296
+ :string, :pointer,
174
297
  :array, :pointer,
175
298
  :rows, :int,
176
299
  :columns, :int
@@ -196,6 +319,7 @@ END
196
319
  end
197
320
  end
198
321
 
322
+ # Put in place the getters
199
323
  if !cells_to_keep || cells_to_keep.empty? || cells_to_keep[name] == :all
200
324
  getable_refs = all_formulae[name].keys
201
325
  elsif !cells_to_keep[name] && settable_refs
@@ -210,6 +334,28 @@ END
210
334
 
211
335
  o.puts " # end of #{name}"
212
336
  end
337
+
338
+ # Now put in place the getters and setters for the named references
339
+ o.puts " # Start of named references"
340
+
341
+ # Getters
342
+ i = input('Named references to keep')
343
+ i.lines.each do |line|
344
+ name = line.strip.split("\t").first
345
+ o.puts " attach_function '#{name}', [], ExcelValue.by_value"
346
+ end
347
+ close(i)
348
+
349
+ # Setters
350
+ i = input('Named references to set')
351
+ i.lines.each do |line|
352
+ name = line.strip.split("\t").first
353
+ o.puts " attach_function 'set_#{name}', [ExcelValue.by_value], :void"
354
+ end
355
+
356
+ close(i)
357
+ o.puts " # End of named references"
358
+
213
359
  o.puts "end"
214
360
  close(o)
215
361
  end
@@ -225,8 +371,8 @@ END
225
371
  o.puts "require_relative '#{output_name.downcase}'"
226
372
  o.puts
227
373
  o.puts "class Test#{ruby_module_name} < Test::Unit::TestCase"
228
- o.puts " def spreadsheet; @spreadsheet ||= init_spreadsheet; end"
229
- o.puts " def init_spreadsheet; #{ruby_module_name} end"
374
+ o.puts " def worksheet; @worksheet ||= init_spreadsheet; end"
375
+ o.puts " def init_spreadsheet; #{ruby_module_name}Shim.new end"
230
376
 
231
377
  all_formulae = all_formulae()
232
378
 
@@ -32,6 +32,18 @@ class ExcelToX
32
32
  # It is a hash. The keys are the sheet names. The values are either the symbol :all to specify that all cells on that sheet
33
33
  # should be setable, or an array of cell names on that sheet that should be settable (e.g., A1)
34
34
  attr_accessor :cells_that_can_be_set_at_runtime
35
+
36
+ # Optional attribute. Specifies which named references to be turned into setters
37
+ #
38
+ # Should be an array of strings. Each string is a named reference. Case sensitive.
39
+ # To specify a named reference scoped to a worksheet, use ['worksheet', 'named reference'] instead
40
+ # of a string.
41
+ #
42
+ # Each named reference then has a function in the resulting C code of the form
43
+ # void set_named_reference_mangled_into_a_c_function(ExcelValue newValue)
44
+ #
45
+ # By default, no named references are output
46
+ attr_accessor :named_references_that_can_be_set_at_runtime
35
47
 
36
48
  # Optional attribute. Specifies which cells must appear in the final generated code.
37
49
  # The default is that all cells in the original spreadsheet appear in the final code.
@@ -45,6 +57,18 @@ class ExcelToX
45
57
  # It is a hash. The keys are the sheet names. The values are either the symbol :all to specify that all cells on that sheet
46
58
  # should be lept, or an array of cell names on that sheet that should be kept (e.g., A1)
47
59
  attr_accessor :cells_to_keep
60
+
61
+ # Optional attribute. Specifies which named references should be included in the output
62
+ # Should be an array of strings. Each string is a named reference. Case sensitive.
63
+ #
64
+ # To specify a named reference scoped to a worksheet, use ['worksheet', 'named reference'] instead
65
+ # of a string.
66
+ #
67
+ # Each named reference then has a function in the resulting C code of the form
68
+ # ExcelValue named_reference_mangled_into_a_c_function()
69
+ #
70
+ # By default, no named references are output
71
+ attr_accessor :named_references_to_keep
48
72
 
49
73
  # Optional attribute. Boolean. Not relevant to all types of code output
50
74
  # * true - the generated c code is compiled
@@ -128,16 +152,22 @@ class ExcelToX
128
152
  # * Mergining all the different types of formulae and values into a single file
129
153
  rewrite_worksheets
130
154
 
155
+ # These perform a series of transformations to the information
156
+ # with the intent of removing any redundant calculations
157
+ # that are in the excel.
158
+ simplify_worksheets # Replacing shared strings and named references with their actual values, tidying arithmetic
159
+
160
+ transfer_named_references_to_keep_into_cells_to_keep
161
+ transfer_named_references_that_can_be_set_at_runtime_into_cells_that_can_be_set_at_runtime
162
+
131
163
  # In case this hasn't been set by the user
132
164
  if cells_that_can_be_set_at_runtime.empty?
133
165
  log.info "Creating a good set of cells that should be settable"
134
166
  create_a_good_set_of_cells_that_should_be_settable_at_runtime
135
167
  end
136
-
137
- # These perform a series of transformations to the information
138
- # with the intent of removing any redundant calculations
139
- # that are in the excel.
140
- simplify_worksheets # Replacing shared strings and named references with their actual values, tidying arithmetic
168
+
169
+ filter_named_references
170
+
141
171
  replace_formulae_with_their_results
142
172
  remove_any_cells_not_needed_for_outputs
143
173
  inline_formulae_that_are_only_used_once
@@ -190,8 +220,9 @@ class ExcelToX
190
220
  def extract_named_references
191
221
  extract ExtractNamedReferences, 'workbook.xml', 'Named references'
192
222
  apply_rewrite RewriteFormulaeToAst, 'Named references'
223
+ replace ReplaceRangesWithArrayLiterals, 'Named references', 'Named references'
193
224
  end
194
-
225
+
195
226
  # Excel keeps a list of worksheet names. To get the mapping between
196
227
  # human and computer name correct we have to look in the workbook
197
228
  # relationships files. We also need to mangle the name into something
@@ -202,7 +233,7 @@ class ExcelToX
202
233
  rewrite RewriteWorksheetNames, 'Worksheet names', 'Workbook relationships', 'Worksheet names'
203
234
  rewrite MapSheetNamesToCNames, 'Worksheet names', 'Worksheet C names'
204
235
  end
205
-
236
+
206
237
  # We want a central list of the maximum extent of each worksheet
207
238
  # so that we can convert column (e.g., C:F) and row (e.g., 13:18)
208
239
  # references into equivalent area references (e.g., C1:F30)
@@ -293,6 +324,10 @@ class ExcelToX
293
324
  end
294
325
  end
295
326
 
327
+ # In Excel we can have references like A:Z and 5:20 which mean all cells in columns
328
+ # A to Z and all cells in rows 5 to 20 respectively. This function translates these
329
+ # into more conventional references (e.g., A5:Z20) based on the maximum area that
330
+ # has been used on a worksheet
296
331
  def rewrite_row_and_column_references(name,xml_filename)
297
332
  dimensions = input('Worksheet dimensions')
298
333
 
@@ -344,6 +379,104 @@ class ExcelToX
344
379
  end
345
380
  required_refs
346
381
  end
382
+
383
+ # Returns a hash of named references, and the ast of their links
384
+ # where the named reference is global the key will be a string of
385
+ # its name and case sensitive.
386
+ # where the named reference is coped to a worksheet, the key will be
387
+ # a two element array. The first element will be the sheet name. The
388
+ # second will be the name.
389
+ def named_references
390
+ return @named_references if @named_references
391
+ @named_references = {}
392
+ i = input('Named references')
393
+ i.lines.each do |line|
394
+ sheet, name, ref = *line.split("\t")
395
+ key = sheet.size != 0 ? [sheet, name] : name
396
+ @named_references[key] = eval(ref)
397
+ end
398
+ close(i)
399
+ @named_references
400
+ end
401
+
402
+ # This makes sure that cells_to_keep includes named_references_to_keep
403
+ def transfer_named_references_to_keep_into_cells_to_keep
404
+ log.debug "Started transfering named references to keep into cells to keep"
405
+ return unless @named_references_to_keep
406
+ @cells_to_keep ||= {}
407
+ all_named_references = named_references
408
+ @named_references_to_keep.each do |name|
409
+ ref = all_named_references[name]
410
+ if ref
411
+ add_ref_to_hash(ref, @cells_to_keep)
412
+ else
413
+ log.warn "Named reference #{name} not found"
414
+ end
415
+ end
416
+ end
417
+
418
+ def transfer_named_references_that_can_be_set_at_runtime_into_cells_that_can_be_set_at_runtime
419
+ log.debug "Started transfering named references that can be set at runtime into cells that can be set at runtime"
420
+ return unless @named_references_that_can_be_set_at_runtime
421
+ @cells_that_can_be_set_at_runtime ||= {}
422
+ all_named_references = named_references
423
+ @named_references_that_can_be_set_at_runtime.each do |name|
424
+ ref = all_named_references[name]
425
+ if ref
426
+ add_ref_to_hash(ref, @cells_that_can_be_set_at_runtime)
427
+ else
428
+ log.warn "Named reference #{name} not found"
429
+ end
430
+ end
431
+ end
432
+
433
+ def add_ref_to_hash(ref, hash)
434
+ if ref.first == :sheet_reference
435
+ sheet = ref[1]
436
+ cell = ref[2][1].gsub('$','')
437
+ hash[sheet] ||= []
438
+ return if hash[sheet] == :all
439
+ hash[sheet] << cell unless hash[sheet].include?(cell)
440
+ elsif ref.first == :array
441
+ ref.shift
442
+ ref.each do |row|
443
+ row.shift
444
+ row.each do |cell|
445
+ add_ref_to_hash(cell, hash)
446
+ end
447
+ end
448
+ else
449
+ log.error "Weird reference in named reference #{ref}"
450
+ end
451
+ end
452
+
453
+ # FIXME: Feels like a kludge
454
+ def filter_named_references
455
+ @named_references_to_keep ||= []
456
+ @named_references_that_can_be_set_at_runtime ||= []
457
+
458
+ i = input('Named references')
459
+ o = intermediate('Named references to keep')
460
+ i.lines.each do |line|
461
+ sheet, name, ref = *line.split("\t")
462
+ key = sheet.length != 0 ? [sheet, name] : name
463
+ o.puts line if named_references_to_keep.include?(key) || named_references_that_can_be_set_at_runtime.include?(key)
464
+ end
465
+ close(o)
466
+
467
+ i.rewind
468
+ o = intermediate('Named references to set')
469
+ i.lines.each do |line|
470
+ sheet, name, ref = *line.split("\t")
471
+ key = sheet.length != 0 ? [sheet, name] : name
472
+ o.puts line if named_references_that_can_be_set_at_runtime.include?(key)
473
+ end
474
+ close(o)
475
+
476
+ # FIXME: Might result in getter and setter having different names
477
+ rewrite RewriteNamedReferenceNames, 'Named references to keep', 'Worksheet C names', 'Named references to keep'
478
+ rewrite RewriteNamedReferenceNames, 'Named references to set', 'Worksheet C names', 'Named references to set'
479
+ end
347
480
 
348
481
  def simplify_worksheets
349
482
  worksheets do |name,xml_filename|
@@ -583,7 +716,8 @@ class ExcelToX
583
716
  close(output)
584
717
  end
585
718
 
586
- # If no settable cells have been specified, then we assume that
719
+ # If nothing has been specified in named_refernces_that_can_be_set_at_runtime
720
+ # or in cells_that_can_be_set_at_runtime, then we assume that
587
721
  # all value cells should be settable if they are referenced by
588
722
  # any other forumla.
589
723
  def create_a_good_set_of_cells_that_should_be_settable_at_runtime
@@ -636,7 +770,7 @@ class ExcelToX
636
770
  i.lines do |line|
637
771
  line =~ /^(.*?)\t(.*)$/
638
772
  ref, ast = $1, $2
639
- r[$1] = eval($2)
773
+ r[ref] = eval(ast)
640
774
  end
641
775
  end
642
776
  references
@@ -3,3 +3,4 @@ require_relative 'c/compile_to_c_header'
3
3
  require_relative 'c/map_sheet_names_to_c_names'
4
4
  require_relative "c/compile_to_c_unit_test"
5
5
  require_relative "c/map_values_to_c_structs"
6
+ require_relative "c/compile_named_reference_setters"
@@ -0,0 +1,90 @@
1
+ class MapNamedReferenceToCSetter
2
+
3
+ attr_accessor :sheet_names
4
+ attr_accessor :cells_that_can_be_set_at_runtime
5
+
6
+ def initialize
7
+ reset
8
+ end
9
+
10
+ def reset
11
+ @new_value_name = "newValue"
12
+ end
13
+
14
+ def map(ast)
15
+ if ast.is_a?(Array)
16
+ operator = ast[0]
17
+ if respond_to?(operator)
18
+ send(operator,*ast[1..-1])
19
+ else
20
+ raise NotSupportedException.new("#{operator} in #{ast.inspect} not supported")
21
+ end
22
+ else
23
+ raise NotSupportedException.new("#{ast} not supported")
24
+ end
25
+ end
26
+
27
+ def cell(reference)
28
+ reference.downcase.gsub('$','')
29
+ end
30
+
31
+ def sheet_reference(sheet,reference)
32
+ s = sheet_names[sheet]
33
+ c = map(reference)
34
+ return " // #{s}_#{c} not settable" unless settable(sheet, c)
35
+ " set_#{s}_#{c}(#{@new_value_name});"
36
+ end
37
+
38
+ def array(*rows)
39
+ counter = -1
40
+
41
+ result = rows.map do |r|
42
+ r.shift if r.first == :row
43
+ r.map do |c|
44
+ counter += 1
45
+ @new_value_name = "array[#{counter}]"
46
+ map(c)
47
+ end
48
+ end.flatten.join("\n")
49
+
50
+ @new_value_name = "newValue"
51
+
52
+ " ExcelValue *array = newValue.array;\n#{result}"
53
+ end
54
+
55
+ def settable(sheet, reference)
56
+ settable_refs = cells_that_can_be_set_at_runtime[sheet]
57
+ return false unless settable_refs
58
+ return true if settable_refs == :all
59
+ settable_refs.include?(reference.upcase)
60
+ end
61
+
62
+ end
63
+
64
+ class CompileNamedReferenceSetters
65
+
66
+ attr_accessor :cells_that_can_be_set_at_runtime
67
+
68
+ def self.rewrite(*args)
69
+ new.rewrite(*args)
70
+ end
71
+
72
+ def rewrite(named_references, sheet_names, output)
73
+ mapper = MapNamedReferenceToCSetter.new
74
+ mapper.sheet_names = Hash[sheet_names.readlines.map { |line| line.strip.split("\t")}]
75
+ mapper.cells_that_can_be_set_at_runtime = cells_that_can_be_set_at_runtime
76
+
77
+ named_references.lines do |line|
78
+ name, reference = line.split("\t")
79
+ ast = eval(reference)
80
+ output.puts "void set_#{name}(ExcelValue newValue) {"
81
+ output.puts mapper.map(ast)
82
+ output.puts "}"
83
+ output.puts
84
+ end
85
+ output
86
+ end
87
+
88
+
89
+
90
+ end
@@ -24,7 +24,7 @@ class CompileToC
24
24
  ref, formula = line.split("\t")
25
25
  ast = eval(formula)
26
26
  calculation = mapper.map(ast)
27
- name = "#{c_name}_#{ref.downcase}"
27
+ name = c_name ? "#{c_name}_#{ref.downcase}" : ref.downcase
28
28
  static_or_not = gettable.call(ref) ? "" : "static "
29
29
  if settable.call(ref)
30
30
  output.puts "ExcelValue #{name}_default() {"
@@ -58,4 +58,4 @@ class CompileToC
58
58
  end
59
59
  end
60
60
 
61
- end
61
+ end
@@ -1,72 +1,7 @@
1
- class CompileToCUnitTest
1
+ require_relative "../ruby/compile_to_ruby_unit_test"
2
2
 
3
- attr_accessor :epsilon
4
- attr_accessor :delta
3
+ class CompileToCUnitTest < CompileToRubyUnitTest
5
4
 
6
- def initialize
7
- @epsilon = 0.001
8
- @delta = 0.001
9
- end
10
-
11
- def self.rewrite(*args)
12
- self.new.rewrite(*args)
13
- end
14
-
15
- def rewrite(input, sloppy, c_name, refs_to_test, output)
16
- input.lines do |line|
17
- begin
18
- ref, formula = line.split("\t")
19
- next unless refs_to_test.include?(ref.upcase)
20
- output.puts "def test_#{c_name}_#{ref.downcase}"
21
- output.puts " r = spreadsheet.#{c_name}_#{ref.downcase}"
22
- ast = eval(formula)
23
- case ast.first
24
- when :number, :percentage
25
- unless sloppy
26
- output.puts " assert_equal(:ExcelNumber,r[:type])"
27
- output.puts " assert_equal(#{ast.last.to_f.to_s},r[:number])"
28
- else
29
- if ast.last.to_f == 0
30
- output.puts " pass if r[:type] == :ExcelEmpty"
31
- end
32
-
33
- output.puts " assert_equal(:ExcelNumber,r[:type])"
34
-
35
- if ast.last.to_f <= 1
36
- output.puts " assert_in_delta(#{ast.last.to_f.to_s},r[:number],#{@delta})"
37
- else
38
- output.puts " assert_in_epsilon(#{ast.last.to_f.to_s},r[:number],#{@epsilon})"
39
- end
40
- end
41
- when :error
42
- output.puts " assert_equal(:ExcelError,r[:type])"
43
- when :string
44
- output.puts " assert_equal(:ExcelString,r[:type])"
45
- output.puts " assert_equal(#{ast.last.inspect},r[:string].force_encoding('utf-8'))"
46
- when :boolean_true
47
- output.puts " assert_equal(:ExcelBoolean,r[:type])"
48
- output.puts " assert_equal(1,r[:number])"
49
- when :boolean_false
50
- output.puts " assert_equal(:ExcelBoolean,r[:type])"
51
- output.puts " assert_equal(0,r[:number])"
52
- when :blank
53
- unless sloppy
54
- output.puts " assert_equal(:ExcelEmpty,r[:type])"
55
- else
56
- output.puts " pass if r[:type] == :ExcelEmpty"
57
- output.puts " assert_equal(:ExcelNumber,r[:type])"
58
- output.puts " assert_in_delta(0.0,r[:number],#{@delta})"
59
- end
60
- else
61
- raise NotSupportedException.new("#{ast} type can't be tested")
62
- end
63
- output.puts "end"
64
- output.puts
65
- rescue Exception => e
66
- puts "Exception at line #{line}"
67
- raise
68
- end
69
- end
70
- end
5
+ # Now produces tests just like the Ruby version
71
6
 
72
7
  end
@@ -1117,6 +1117,7 @@ static ExcelValue string_join(int number_of_arguments, ExcelValue *arguments) {
1117
1117
  used_length = used_length + current_string_length;
1118
1118
  }
1119
1119
  string = realloc(string,used_length+1);
1120
+ string[used_length] = '\0';
1120
1121
  return new_excel_string(string);
1121
1122
  }
1122
1123
 
@@ -1878,13 +1879,20 @@ int test_functions() {
1878
1879
  // ... should return a string by combining its arguments
1879
1880
  // inspect_excel_value(string_join(2, string_join_array_1));
1880
1881
  assert(string_join(2, string_join_array_1).string[6] == 'w');
1882
+ assert(string_join(2, string_join_array_1).string[11] == '\0');
1881
1883
  // ... should cope with an arbitrary number of arguments
1882
1884
  assert(string_join(3, string_join_array_2).string[11] == '!');
1885
+ assert(string_join(3, string_join_array_3).string[12] == '\0');
1883
1886
  // ... should convert values to strings as it goes
1884
1887
  assert(string_join(2, string_join_array_3).string[4] == '1');
1885
- // ... should convert integer values into strings without decimal points
1886
- assert(string_join(2, string_join_array_3).string[7] == '\0');
1888
+ assert(string_join(2, string_join_array_3).string[5] == '0');
1889
+ assert(string_join(2, string_join_array_3).string[6] == '\0');
1890
+ // ... should convert integer values into strings without decimal points, and float values with decimal points
1891
+ assert(string_join(2, string_join_array_4).string[4] == '1');
1892
+ assert(string_join(2, string_join_array_4).string[5] == '0');
1893
+ assert(string_join(2, string_join_array_4).string[6] == '.');
1887
1894
  assert(string_join(2, string_join_array_4).string[7] == '5');
1895
+ assert(string_join(2, string_join_array_4).string[8] == '\0');
1888
1896
  // ... should convert TRUE and FALSE into strings
1889
1897
  assert(string_join(3,string_join_array_5).string[4] == 'T');
1890
1898
 
@@ -10,7 +10,7 @@ class MapSheetNamesToCNames
10
10
  excel_worksheet_name = line.split("\t").first
11
11
  c_name = excel_worksheet_name.downcase.gsub(/[^a-z0-9]+/,'_')
12
12
  c_name = "s"+c_name if c_name[0] !~ /[a-z]/
13
- c_name = ruby_name + "2" if c_names_assigned.has_key?(c_name)
13
+ c_name = c_name + "2" if c_names_assigned.has_key?(c_name)
14
14
  c_name.succ! while c_names_assigned.has_key?(c_name)
15
15
  output.puts "#{excel_worksheet_name}\t#{c_name}"
16
16
  c_names_assigned[c_name] = excel_worksheet_name
@@ -8,3 +8,4 @@ require_relative "rewrite/rewrite_values_to_ast"
8
8
  require_relative "rewrite/rewrite_relationship_id_to_filename"
9
9
  require_relative "rewrite/rewrite_merge_formulae_and_values"
10
10
  require_relative "rewrite/rewrite_cell_references_to_include_sheet"
11
+ require_relative "rewrite/rewrite_named_reference_names"
@@ -0,0 +1,35 @@
1
+ class RewriteNamedReferenceNames
2
+
3
+ def self.rewrite(*args)
4
+ self.new.rewrite(*args)
5
+ end
6
+
7
+ # Expects named reference in the form:
8
+ # sheet_name_or_blank_for_global\tnamed_reference_name\treference\n
9
+ # Outputs named references in the form:
10
+ # name\treference\n
11
+ # where name incorporates the sheet name and has been rewritten in a way
12
+ # that works as a c function name and (hopefully) won't clash with any
13
+ # existing names
14
+ # FIXME: but could still clash with function names and methods in the ruby shim
15
+ def rewrite(named_references, worksheet_names, output)
16
+ worksheet_names = Hash[worksheet_names.readlines.map { |line| line.strip.split("\t")}]
17
+ c_names_assigned = worksheet_names.invert
18
+
19
+ named_references.lines do |line|
20
+ sheet, name, reference = line.split("\t")
21
+ sheet = worksheet_names[sheet]
22
+ if sheet
23
+ c_name = "#{sheet}_#{name}"
24
+ else
25
+ c_name = name
26
+ c_name = "n"+c_name if c_name[0] !~ /[a-zA-Z]/
27
+ end
28
+ c_name = c_name.downcase.gsub(/[^a-z0-9]+/,'_')
29
+ c_name = c_name + "2" if c_names_assigned.has_key?(c_name)
30
+ c_name.succ! while c_names_assigned.has_key?(c_name)
31
+ output.puts "#{c_name}\t#{reference}"
32
+ c_names_assigned[c_name] = c_name
33
+ end
34
+ end
35
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: excel_to_code
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.14
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-17 00:00:00.000000000 Z
12
+ date: 2012-11-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rubypeg
@@ -90,7 +90,8 @@ description: ! "# excel_to_code\n\nConverts some excel spreadsheets (.xlsx, not
90
90
  INDIRECT formula must be convertable at runtime into a standard formula\n3. Doesn't
91
91
  implement all functions (see doc/Which_functions_are_implemented.md)\n4. Doesn't
92
92
  implement references that involve range unions and lists\n5. Sometimes gives cells
93
- as being empty, when excel would give the cell as having a numeric value of zero\n"
93
+ as being empty, when excel would give the cell as having a numeric value of zero\n6.
94
+ The generated C version does not multithread and will give bad results if you try\n"
94
95
  email: tamc@greenonblack.com
95
96
  executables:
96
97
  - excel_to_c
@@ -104,6 +105,7 @@ files:
104
105
  - src/commands/excel_to_ruby.rb
105
106
  - src/commands/excel_to_x.rb
106
107
  - src/commands.rb
108
+ - src/compile/c/compile_named_reference_setters.rb
107
109
  - src/compile/c/compile_to_c.rb
108
110
  - src/compile/c/compile_to_c_header.rb
109
111
  - src/compile/c/compile_to_c_unit_test.rb
@@ -194,6 +196,7 @@ files:
194
196
  - src/rewrite/rewrite_cell_references_to_include_sheet.rb
195
197
  - src/rewrite/rewrite_formulae_to_ast.rb
196
198
  - src/rewrite/rewrite_merge_formulae_and_values.rb
199
+ - src/rewrite/rewrite_named_reference_names.rb
197
200
  - src/rewrite/rewrite_relationship_id_to_filename.rb
198
201
  - src/rewrite/rewrite_shared_formulae.rb
199
202
  - src/rewrite/rewrite_values_to_ast.rb