excel_to_code 0.0.14 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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