excel_to_code 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,7 +3,7 @@
3
3
  Converts some excel spreadsheets (.xlsx, not .xls) into some other programming languages (currently ruby or c).
4
4
  This allows the excel spreadsheets to be run programatically, without excel.
5
5
 
6
- Its cannonical source is at http://github.com/tamc/excel2code
6
+ Its cannonical source is at http://github.com/tamc/excel_to_code
7
7
 
8
8
  # Running excel_to_code
9
9
 
data/TODO ADDED
@@ -0,0 +1,34 @@
1
+ # TODO
2
+
3
+ ## Parsing bugs
4
+
5
+ See doc/How_to_fix_parsing_bugs.md for help
6
+
7
+ * Whitespace handling in table references
8
+ * Whitespace handling between arguments in a list
9
+ * Doesn't do range unions (e.g., 10:10 C:C == C:10)
10
+ * Doesn't do manually entered arrays {1,2,3;4,5,6}
11
+
12
+ ## Missing functions
13
+
14
+ See doc/Which_functions_are_implemented.md
15
+
16
+ See doc/How_to_add_a_missing_function.md
17
+
18
+ ## Simplification & optimisation bugs
19
+
20
+ * Optimize IF, CHOOSE, MATCH, VLOOKUP and similar functions so that they don't have to calculate all their arguments
21
+ * Fix it so that cells that are being reported as empty, that excel would give a numeric value of zero, are fixed
22
+
23
+ ## Things that are badly written
24
+
25
+ * Rewrite the excel_to_ruby command to use command line options
26
+ * Refactor the common elements of src/commands/excel_to_c.rb and src/commands/excel_to_ruby.rb
27
+ * Refactor excel_to_c_runtime - split the functions? split the tests?
28
+ * Figure out a common test framework for different code output
29
+ * Tool for turning spreadsheets into tests
30
+ * Option to reorder test output to make it easier to localise errors
31
+ * Tool to create minimal failing example out of test run
32
+
33
+
34
+
data/bin/excel_to_c CHANGED
@@ -13,7 +13,7 @@ Usage: excel_to_c [options] input_excel_file <output_directory>
13
13
  input_excel_file the name of a .xlsx excel file (i.e., produced by Excel 2007+, not a .xls file)
14
14
  output_directory the name of a folder in which to place the generated code. If not specified will have the same name as the input_excel_file, without the .xlsx
15
15
 
16
- Support: http://github.com/tamc/excel2code
16
+ Support: http://github.com/tamc/excel_to_code
17
17
  END
18
18
 
19
19
  opts.separator ""
data/bin/excel_to_ruby CHANGED
@@ -1,9 +1,63 @@
1
1
  #!/usr/bin/env ruby
2
- require_relative '../src/commands/excel_to_ruby'
2
+ require 'optparse'
3
+ require_relative '../src/excel_to_code'
4
+
3
5
  command = ExcelToRuby.new
6
+
7
+ opts = OptionParser.new do |opts|
8
+ opts.banner = <<-END
9
+
10
+ A command to approximately translate excel files into c code.
11
+
12
+ Usage: excel_to_ruby [options] input_excel_file <output_directory>
13
+ input_excel_file the name of a .xlsx excel file (i.e., produced by Excel 2007+, not a .xls file)
14
+ output_directory the name of a folder in which to place the generated code. If not specified will have the same name as the input_excel_file, without the .xlsx
15
+
16
+ Support: http://github.com/tamc/excel_to_code
17
+ END
18
+
19
+ opts.separator ""
20
+ opts.separator "Specific options:"
21
+
22
+ opts.on('-s','--settable WORKSHEET','Make it possible to set the values of cells in this worksheet at runtime. By default no values are settable.') do |sheet|
23
+ command.cells_that_can_be_set_at_runtime = { sheet => :all }
24
+ end
25
+
26
+ opts.on('-o','--output-name NAME','Filename to give to ruby version of code. Defaults to a folder with the same name as the excel file.') do |name|
27
+ command.output_name = name
28
+ end
29
+
30
+ opts.on('-p','--prune-except WORKSHEET',"Remove all cells except those on this worksheet, or that are required to calculate values on that worksheet. By default keeps all cells.") do |sheet|
31
+ command.cells_to_keep = { sheet => :all }
32
+ end
33
+
34
+ opts.on('-r','--run-tests',"Run the generated tests") do
35
+ command.actually_run_tests = true
36
+ end
37
+
38
+ opts.on('-m','--run-in-memory',"Instead of writing intermediate files to disk, uses memory. Requires a lot of memory") do
39
+ command.run_in_memory = true
40
+ end
41
+
42
+ opts.on("-h", "--help", "Show this message") do
43
+ puts opts
44
+ exit
45
+ end
46
+ end
47
+
48
+ begin
49
+ opts.parse!(ARGV)
50
+ rescue OptionParser::ParseError => e
51
+ STDERR.puts e.message, "\n", opts
52
+ exit(-1)
53
+ end
54
+
55
+ unless ARGV.size > 0
56
+ puts opts
57
+ exit(-1)
58
+ end
59
+
4
60
  command.excel_file = ARGV[0]
5
- command.compiled_module_name = "ExcelSpreadsheet"
6
- command.output_directory = ARGV[1]
7
- command.values_that_can_be_set_at_runtime = { ARGV[2] => :all } if ARGV[2]
8
- command.outputs_to_keep = { ARGV[3] => :all } if ARGV[3]
9
- command.go!
61
+ command.output_directory = ARGV[1] if ARGV[1]
62
+
63
+ command.go!
@@ -3,6 +3,10 @@ require_relative 'excel_to_x'
3
3
 
4
4
  class ExcelToC < ExcelToX
5
5
 
6
+ def language
7
+ "c"
8
+ end
9
+
6
10
  # These actually create the code version of the excel
7
11
  def write_code
8
12
  write_out_excel_as_code
@@ -18,13 +22,18 @@ class ExcelToC < ExcelToX
18
22
  number_of_refs = 0
19
23
 
20
24
  # Probably a better way of getting the runtime file to be compiled with the created file
21
- puts `cp #{File.join(File.dirname(__FILE__),'..','compile','c','excel_to_c_runtime.c')} #{File.join(output_directory,'excel_to_c_runtime.c')}`
25
+ puts `cp #} #{File.join(output_directory,'excel_to_c_runtime.c')}`
22
26
 
23
27
  # Output the workbook preamble
24
28
  w = input("worksheet_c_names")
25
29
  o = output("#{output_name.downcase}.c")
26
30
  o.puts "// #{excel_file} approximately translated into C"
27
- o.puts '#include "excel_to_c_runtime.c"'
31
+
32
+ o.puts '// First we have c versions of all the excel functions that we know'
33
+ o.puts IO.readlines(File.join(File.dirname(__FILE__),'..','compile','c','excel_to_c_runtime.c')).join
34
+ o.puts '// End of the generic c functions'
35
+ o.puts
36
+ o.puts '// Start of the file specific functions'
28
37
  o.puts
29
38
 
30
39
  # Now we have to put all the initial definitions out
@@ -109,7 +118,6 @@ class ExcelToC < ExcelToX
109
118
  c.worksheet = name
110
119
 
111
120
  i = input(name,"formulae_inlined_pruned_replaced.ast")
112
- ruby_name = c_name_for_worksheet_name(name)
113
121
  o.puts "// start #{name}"
114
122
  c.rewrite(i,w,o)
115
123
  o.puts "// end #{name}"
@@ -145,12 +153,13 @@ class ExcelToC < ExcelToX
145
153
  all_formulae = all_formulae('formulae_inlined_pruned_replaced.ast')
146
154
  name = output_name.downcase
147
155
  o = output("#{name}.rb")
156
+
148
157
  code = <<END
149
158
  require 'ffi'
150
159
 
151
- module #{name.capitalize}
160
+ module #{ruby_module_name}
152
161
  extend FFI::Library
153
- ffi_lib File.join(File.dirname(__FILE__),'lib#{name}.dylib')
162
+ ffi_lib File.join(File.dirname(__FILE__),FFI.map_library_name('#{name}'))
154
163
  ExcelType = enum :ExcelEmpty, :ExcelNumber, :ExcelString, :ExcelBoolean, :ExcelError, :ExcelRange
155
164
 
156
165
  class ExcelValue < FFI::Struct
@@ -202,7 +211,7 @@ END
202
211
 
203
212
  def write_tests
204
213
  name = output_name.downcase
205
- o = output("#{name}_test.rb")
214
+ o = output("test_#{name}.rb")
206
215
  o.puts "# coding: utf-8"
207
216
  o.puts "# Test for #{name}"
208
217
  o.puts "require 'rubygems'"
@@ -212,7 +221,7 @@ END
212
221
  o.puts
213
222
  o.puts "class Test#{name.capitalize} < Test::Unit::TestCase"
214
223
  o.puts " def spreadsheet; @spreadsheet ||= init_spreadsheet; end"
215
- o.puts " def init_spreadsheet; #{name.capitalize} end"
224
+ o.puts " def init_spreadsheet; #{ruby_module_name} end"
216
225
 
217
226
  all_formulae = all_formulae('formulae_inlined_pruned_replaced.ast')
218
227
 
@@ -227,6 +236,7 @@ END
227
236
  refs_to_test = cells_to_keep[name]
228
237
  end
229
238
  if refs_to_test && !refs_to_test.empty?
239
+ refs_to_test = refs_to_test.map(&:upcase)
230
240
  CompileToCUnitTest.rewrite(i, c_name, refs_to_test, o)
231
241
  end
232
242
  close(i)
@@ -244,7 +254,7 @@ END
244
254
  def run_tests
245
255
  return unless actually_run_tests
246
256
  puts "Running the resulting tests"
247
- puts `cd #{File.join(output_directory)}; ruby "#{output_name.downcase}_test.rb"`
257
+ puts `cd #{File.join(output_directory)}; ruby "test_#{output_name.downcase}.rb"`
248
258
  end
249
259
 
250
260
  end
@@ -3,118 +3,133 @@
3
3
  require_relative 'excel_to_x'
4
4
 
5
5
  class ExcelToRuby < ExcelToX
6
+
7
+ def language
8
+ "ruby"
9
+ end
10
+
11
+ # Skip this
12
+ def replace_values_with_constants
6
13
 
14
+ worksheets("Skipping replacing values with constants") do |name,xml_filename|
15
+ i = File.join(intermediate_directory, name, "formulae_inlined_pruned_replaced-1.ast")
16
+ o = File.join(intermediate_directory, name, "formulae_inlined_pruned_replaced.ast")
17
+ if run_in_memory
18
+ @files[o] = @files[i]
19
+ else
20
+ `cp '#{i}' '#{o}'`
21
+ end
22
+ end
23
+
24
+ i = File.join(intermediate_directory,"common-elements-1.ast")
25
+ o = File.join(intermediate_directory,"common-elements.ast")
26
+ if run_in_memory
27
+ @files[o] = @files[i]
28
+ else
29
+ `cp '#{i}' '#{o}'`
30
+ end
31
+ end
32
+
7
33
  # These actually create the code version of the excel
8
34
  def write_code
9
- write_out_excel_workbook_as_code
10
- write_out_excel_workbook_test_as_code
11
- worksheets("Compiling worksheet") do |name,xml_filename|
12
- #fork do
13
- compile_worksheet_code(name,xml_filename)
14
- compile_worksheet_test(name,xml_filename)
15
- #end
16
- end
35
+ write_out_excel_as_code
36
+ write_out_test_as_code
17
37
  end
18
38
 
19
- def write_out_excel_workbook_as_code
20
- w = input("worksheet_ruby_names")
21
- o = ruby("#{compiled_module_name.downcase}.rb")
39
+ def write_out_excel_as_code
40
+ w = input("worksheet_c_names")
41
+ o = output("#{output_name.downcase}.rb")
42
+ o.puts "# coding: utf-8"
22
43
  o.puts "# Compiled version of #{excel_file}"
23
44
  o.puts "require '#{File.expand_path(File.join(File.dirname(__FILE__),'../excel/excel_functions'))}'"
24
45
  o.puts ""
25
- o.puts "module #{compiled_module_name}"
26
- o.puts "class Spreadsheet"
46
+ o.puts "class #{ruby_module_name}"
27
47
  o.puts " include ExcelFunctions"
28
- w.lines do |line|
29
- name, ruby_name = line.strip.split("\t")
30
- o.puts " def #{ruby_name}; @#{ruby_name} ||= #{ruby_name.capitalize}.new; end"
31
- end
32
48
 
49
+ o.puts
50
+ o.puts " # Starting common elements"
33
51
  c = CompileToRuby.new
34
52
  i = input("common-elements.ast")
35
- w.rewind
53
+ w.rewind
36
54
  c.rewrite(i,w,o)
37
-
38
- o.puts "end"
39
- o.puts 'Dir[File.join(File.dirname(__FILE__),"worksheets/","*.rb")].each {|f| autoload(File.basename(f,".rb").capitalize,f)}'
40
- o.puts "end"
41
- close(i,w,o)
42
- end
43
-
44
- def write_out_excel_workbook_test_as_code
45
- w = input("worksheet_ruby_names")
46
- o = ruby("test_#{compiled_module_name.downcase}.rb")
47
- o.puts "# All tests for #{excel_file}"
48
- o.puts "require 'test/unit'"
49
- w.lines do |line|
50
- name, ruby_name = line.strip.split("\t")
51
- o.puts "require_relative 'tests/test_#{ruby_name.downcase}'"
52
- end
53
- close(w,o)
54
- end
55
-
56
- def compile_worksheet_code(name,xml_filename)
57
- settable_refs = @values_that_can_be_set_at_runtime[name]
58
- c = CompileToRuby.new
59
- c.settable =lambda { |ref| (settable_refs == :all) ? true : settable_refs.include?(ref) } if settable_refs
60
- c.worksheet = name
61
- i = input(name,"formulae_inlined_pruned_replaced.ast")
62
- w = input("worksheet_ruby_names")
63
- ruby_name = ruby_name_for_worksheet_name(name)
64
- o = ruby('worksheets',"#{ruby_name.downcase}.rb")
65
- d = output(name,'defaults')
66
- o.puts "# coding: utf-8"
67
- o.puts "# #{name}"
55
+ o.puts " # Ending common elements"
68
56
  o.puts
69
- o.puts "require_relative '../#{compiled_module_name.downcase}'"
57
+ close(i)
58
+
59
+ d = intermediate('defaults')
60
+
61
+ worksheets("Turning worksheet into code") do |name,xml_filename|
62
+ c.settable = settable(name)
63
+ c.worksheet = name
64
+ i = input(name,"formulae_inlined_pruned_replaced.ast")
65
+ w.rewind
66
+ o.puts " # Start of #{name}"
67
+ c.rewrite(i,w,o,d)
68
+ o.puts " # End of #{name}"
69
+ o.puts ""
70
+ close(i)
71
+ end
72
+
73
+ close(d)
74
+
70
75
  o.puts
71
- o.puts "module #{compiled_module_name}"
72
- o.puts "class #{ruby_name.capitalize} < Spreadsheet"
73
- c.rewrite(i,w,o,d)
76
+ o.puts " # starting initializer"
77
+ o.puts " def initialize"
78
+ d = input('defaults')
79
+ d.lines do |line|
80
+ o.puts line
81
+ end
82
+ o.puts " end"
74
83
  o.puts ""
75
84
  close(d)
76
- if settable_refs
77
- o.puts " def initialize"
78
- d = input(name,'defaults')
79
- d.lines do |line|
80
- o.puts line
81
- end
82
- o.puts " end"
83
- o.puts ""
84
- close(d)
85
- end
86
- o.puts "end"
85
+
87
86
  o.puts "end"
88
- close(i,o)
87
+ close(w,o)
89
88
  end
90
89
 
91
- def compile_worksheet_test(name,xml_filename)
92
- i = input(name,"values_pruned2.ast")
93
- ruby_name = ruby_name_for_worksheet_name(name)
94
- o = ruby('tests',"test_#{ruby_name.downcase}.rb")
90
+ def write_out_test_as_code
91
+ o = output("test_#{output_name.downcase}.rb")
92
+
95
93
  o.puts "# coding: utf-8"
96
- o.puts "# Test for #{name}"
97
- o.puts "require 'test/unit'"
98
- o.puts "require_relative '../#{compiled_module_name.downcase}'"
94
+ o.puts "# All tests for #{excel_file}"
95
+ o.puts "require 'test/unit'"
96
+ o.puts "require_relative '#{output_name.downcase}'"
99
97
  o.puts
100
- o.puts "module #{compiled_module_name}"
101
- o.puts "class Test#{ruby_name.capitalize} < Test::Unit::TestCase"
102
- o.puts " def spreadsheet; $spreadsheet ||= Spreadsheet.new; end"
103
- o.puts " def worksheet; @worksheet ||= spreadsheet.#{ruby_name}; end"
104
- CompileToRubyUnitTest.rewrite(i, o)
105
- o.puts "end"
106
- o.puts "end"
107
- close(i,o)
98
+ o.puts "class Test#{output_name.capitalize} < Test::Unit::TestCase"
99
+ o.puts " def worksheet; @worksheet ||= #{ruby_module_name}.new; end"
100
+
101
+ c = CompileToRubyUnitTest.new
102
+ all_formulae = all_formulae('formulae_inlined_pruned_replaced.ast')
103
+
104
+ worksheets("Compiling worksheet") do |name,xml_filename|
105
+ i = input(name,"values_pruned2.ast")
106
+ o.puts " # Start of #{name}"
107
+ c_name = c_name_for_worksheet_name(name)
108
+ if !cells_to_keep || cells_to_keep.empty? || cells_to_keep[name] == :all
109
+ refs_to_test = all_formulae[name].keys
110
+ else
111
+ refs_to_test = cells_to_keep[name]
112
+ end
113
+ if refs_to_test && !refs_to_test.empty?
114
+ refs_to_test = refs_to_test.map(&:upcase)
115
+ c.rewrite(i, c_name, refs_to_test, o)
116
+ end
117
+ o.puts " # End of #{name}"
118
+ o.puts ""
119
+ close(i)
120
+ end
121
+ o.puts "end"
122
+ close(o)
108
123
  end
109
124
 
110
- def ruby_name_for_worksheet_name(name)
111
- unless @worksheet_names
112
- w = input("worksheet_ruby_names")
113
- @worksheet_names = Hash[w.readlines.map { |line| line.split("\t").map { |a| a.strip }}]
114
- close(w)
115
- end
116
- @worksheet_names[name]
125
+ def compile_code
126
+ # Not needed
127
+ end
128
+
129
+ def run_tests
130
+ return unless actually_run_tests
131
+ puts "Running the resulting tests"
132
+ puts `cd #{File.join(output_directory)}; ruby "test_#{output_name.downcase}.rb"`
117
133
  end
118
-
119
134
 
120
135
  end
@@ -14,7 +14,7 @@ class ExcelToX
14
14
  # If not specified, will be '#{excel_file_name}/c'
15
15
  attr_accessor :output_directory
16
16
 
17
- # Optional attribute. The name of the resulting c file (and associated ruby ffi module). Defaults to excelspreadsheet
17
+ # Optional attribute. The name of the resulting ruby or c file and ruby or ruby ffi module name. Defaults to excelspreadsheet
18
18
  attr_accessor :output_name
19
19
 
20
20
  # Optional attribute. The excel file will be translated to xml and stored here.
@@ -61,7 +61,7 @@ class ExcelToX
61
61
  def set_defaults
62
62
  raise ExcelToCodeException.new("No excel file has been specified") unless excel_file
63
63
 
64
- self.output_directory ||= File.join(File.dirname(excel_file),File.basename(excel_file,".*"),'c')
64
+ self.output_directory ||= File.join(File.dirname(excel_file),File.basename(excel_file,".*"),language)
65
65
  self.xml_directory ||= File.join(File.dirname(excel_file),File.basename(excel_file,".*"),'xml')
66
66
  self.intermediate_directory ||= File.join(File.dirname(excel_file),File.basename(excel_file,".*"),'intermediate')
67
67
 
@@ -72,14 +72,14 @@ class ExcelToX
72
72
  # Make sure that all the cell names are downcase and don't have any $ in them
73
73
  cells_that_can_be_set_at_runtime.keys.each do |sheet|
74
74
  next unless cells_that_can_be_set_at_runtime[sheet].is_a?(Array)
75
- cells_that_can_be_set_at_runtime[sheet] = cells_that_can_be_set_at_runtime[sheet].map { |reference| reference.gsub('$','').downcase }
75
+ cells_that_can_be_set_at_runtime[sheet] = cells_that_can_be_set_at_runtime[sheet].map { |reference| reference.gsub('$','').upcase }
76
76
  end
77
77
 
78
78
  # Make sure that all the cell names are downcase and don't have any $ in them
79
79
  if cells_to_keep
80
80
  cells_to_keep.keys.each do |sheet|
81
81
  next unless cells_to_keep[sheet].is_a?(Array)
82
- cells_to_keep[sheet] = cells_to_keep[sheet].map { |reference| reference.gsub('$','').downcase }
82
+ cells_to_keep[sheet] = cells_to_keep[sheet].map { |reference| reference.gsub('$','').upcase }
83
83
  end
84
84
  end
85
85
 
@@ -113,6 +113,7 @@ class ExcelToX
113
113
  remove_any_cells_not_needed_for_outputs
114
114
  inline_formulae_that_are_only_used_once
115
115
  separate_formulae_elements
116
+ replace_values_with_constants
116
117
 
117
118
  # This actually creates the code (implemented in subclasses)
118
119
  write_code
@@ -272,7 +273,21 @@ class ExcelToX
272
273
  array_formulae = File.join(name,"array_formulae-expanded.ast")
273
274
  simple_formulae = File.join(name,"simple_formulae.ast-nocols")
274
275
  output = File.join(name,'formulae.ast')
275
- rewrite RewriteMergeFormulaeAndValues, values, shared_formulae, array_formulae, simple_formulae, output
276
+
277
+ # This ensures that all gettable and settable values appear in the output
278
+ # even if they are blank in the underlying excel
279
+ required_refs = []
280
+ if @cells_that_can_be_set_at_runtime && @cells_that_can_be_set_at_runtime[name] && @cells_that_can_be_set_at_runtime[name] != :all
281
+ required_refs.concat(@cells_that_can_be_set_at_runtime[name])
282
+ end
283
+ if @cells_to_keep && @cells_to_keep[name] && @cells_to_keep[name] != :all
284
+ required_refs.concat(@cells_to_keep[name])
285
+ end
286
+
287
+ r = RewriteMergeFormulaeAndValues.new
288
+ r.references_to_add_if_they_are_not_already_present = required_refs
289
+
290
+ rewrite r, values, shared_formulae, array_formulae, simple_formulae, output
276
291
  end
277
292
 
278
293
  def merge_table_files
@@ -516,7 +531,9 @@ class ExcelToX
516
531
  worksheets("Replacing repeated elements") do |name,xml_filename|
517
532
  replace ReplaceCommonElementsInFormulae, File.join(name,"formulae_inlined_pruned_with_sheets.ast"), "common-elements-1.ast", File.join(name,"formulae_inlined_pruned_replaced-1.ast")
518
533
  end
519
-
534
+ end
535
+
536
+ def replace_values_with_constants
520
537
  r = ReplaceValuesWithConstants.new
521
538
  worksheets("Replacing values with constants") do |name,xml_filename|
522
539
  i = input(name,"formulae_inlined_pruned_replaced-1.ast")
@@ -544,7 +561,7 @@ class ExcelToX
544
561
  def settable(name)
545
562
  settable_refs = @cells_that_can_be_set_at_runtime[name]
546
563
  if settable_refs
547
- lambda { |ref| (settable_refs == :all) ? true : settable_refs.include?(ref) }
564
+ lambda { |ref| (settable_refs == :all) ? true : settable_refs.include?(ref.upcase) }
548
565
  else
549
566
  lambda { |ref| false }
550
567
  end
@@ -554,7 +571,7 @@ class ExcelToX
554
571
  if @cells_to_keep
555
572
  gettable_refs = @cells_to_keep[name]
556
573
  if gettable_refs
557
- lambda { |ref| (gettable_refs == :all) ? true : gettable_refs.include?(ref) }
574
+ lambda { |ref| (gettable_refs == :all) ? true : gettable_refs.include?(ref.upcase) }
558
575
  else
559
576
  lambda { |ref| false }
560
577
  end
@@ -657,4 +674,12 @@ class ExcelToX
657
674
  end
658
675
  end
659
676
 
677
+ def ruby_module_name
678
+ puts output_name
679
+ @ruby_module_name = output_name.sub(/^[a-z\d]*/) { $&.capitalize }
680
+ @ruby_module_name = @ruby_module_name.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }.gsub('/', '::')
681
+ puts @ruby_module_name
682
+ @ruby_module_name
683
+ end
684
+
660
685
  end
@@ -43,7 +43,6 @@ class CompileToC
43
43
  mapper.initializers.each do |i|
44
44
  output.puts " #{i}"
45
45
  end
46
- #output.puts " return #{calculation};"
47
46
  output.puts " result = #{calculation};"
48
47
  output.puts " variable_set[#{@variable_set_counter}] = 1;"
49
48
  output.puts " return result;"
@@ -8,7 +8,7 @@ class CompileToCUnitTest
8
8
  input.lines do |line|
9
9
  begin
10
10
  ref, formula = line.split("\t")
11
- next unless refs_to_test.include?(ref)
11
+ next unless refs_to_test.include?(ref.upcase)
12
12
  output.puts "def test_#{c_name}_#{ref.downcase}"
13
13
  output.puts " r = spreadsheet.#{c_name}_#{ref.downcase}"
14
14
  ast = eval(formula)
@@ -14,14 +14,16 @@ class CompileToRuby
14
14
  mapper = MapFormulaeToRuby.new
15
15
  mapper.worksheet = worksheet
16
16
  mapper.sheet_names = Hash[sheet_names_file.readlines.map { |line| line.strip.split("\t")}]
17
+ c_name = mapper.sheet_names[worksheet]
17
18
  input.lines do |line|
18
19
  begin
19
20
  ref, formula = line.split("\t")
21
+ name = c_name ? "#{c_name}_#{ref.downcase}" : ref.downcase
20
22
  if settable.call(ref)
21
- output.puts " attr_accessor :#{ref.downcase} # Default: #{mapper.map(eval(formula))}"
22
- defaults.puts " @#{ref.downcase} = #{mapper.map(eval(formula))}" if defaults
23
+ output.puts " attr_accessor :#{name} # Default: #{mapper.map(eval(formula))}"
24
+ defaults.puts " @#{name} = #{mapper.map(eval(formula))}" if defaults
23
25
  else
24
- output.puts " def #{ref.downcase}; @#{ref.downcase} ||= #{mapper.map(eval(formula))}; end"
26
+ output.puts " def #{name}; @#{name} ||= #{mapper.map(eval(formula))}; end"
25
27
  end
26
28
  rescue Exception => e
27
29
  puts "Exception at line #{line}"
@@ -6,21 +6,22 @@ class CompileToRubyUnitTest
6
6
  self.new.rewrite(*args)
7
7
  end
8
8
 
9
- def rewrite(input,output)
9
+ def rewrite(input, c_name, refs_to_test, output)
10
10
  mapper = MapValuesToRuby.new
11
11
  input.lines do |line|
12
12
  ref, formula = line.split("\t")
13
+ next unless refs_to_test.include?(ref.upcase)
13
14
  ast = eval(formula)
14
15
  value = mapper.map(ast)
15
- full_reference = "worksheet.#{ref.downcase}"
16
+ full_reference = "worksheet.#{c_name}_#{ref.downcase}"
16
17
  if ast.first == :number
17
18
  if value == "0" # Need to do a slightly different test, because needs to pass if nil returned, as well as zero
18
- output.puts " def test_#{ref.downcase}; assert_in_epsilon(#{value},#{full_reference} || 0); end"
19
+ output.puts " def test_#{c_name}_#{ref.downcase}; assert_in_epsilon(#{value},#{full_reference} || 0); end"
19
20
  else
20
- output.puts " def test_#{ref.downcase}; assert_in_epsilon(#{value},#{full_reference}); end"
21
+ output.puts " def test_#{c_name}_#{ref.downcase}; assert_in_epsilon(#{value},#{full_reference}); end"
21
22
  end
22
23
  else
23
- output.puts " def test_#{ref.downcase}; assert_equal(#{value},#{full_reference}); end"
24
+ output.puts " def test_#{c_name}_#{ref.downcase}; assert_equal(#{value},#{full_reference}); end"
24
25
  end
25
26
  end
26
27
  end
@@ -80,8 +80,7 @@ class MapFormulaeToRuby < MapValuesToRuby
80
80
  end
81
81
 
82
82
  def sheet_reference(sheet,reference)
83
- return map(reference) if worksheet && worksheet == sheet
84
- "#{sheet_names[sheet]}.#{map(reference)}"
83
+ "#{sheet_names[sheet]}_#{map(reference)}"
85
84
  end
86
85
 
87
86
  def array(*rows)
@@ -3,15 +3,23 @@ class RewriteMergeFormulaeAndValues
3
3
  new.rewrite(*args)
4
4
  end
5
5
 
6
+ attr_accessor :references_to_add_if_they_are_not_already_present
7
+
6
8
  def rewrite(values,shared_formulae,array_formula,simple_formulae,output)
9
+ @references_to_add_if_they_are_not_already_present ||= []
10
+
7
11
  shared_formulae = Hash[shared_formulae.readlines.map { |line| [line[/(.*?)\t/,1],line]}]
8
12
  array_formula = Hash[array_formula.readlines.map { |line| [line[/(.*?)\t/,1],line]}]
9
13
  simple_formulae = Hash[simple_formulae.readlines.map { |line| [line[/(.*?)\t/,1],line]}]
10
14
 
11
15
  values.lines do |line|
12
16
  ref = line[/(.*?)\t/,1]
17
+ @references_to_add_if_they_are_not_already_present.delete(ref)
13
18
  output.puts simple_formulae[ref] || array_formula[ref] || shared_formulae[ref] || line
14
19
  end
20
+ @references_to_add_if_they_are_not_already_present.each do |r|
21
+ output.puts "#{r}\t[:blank]"
22
+ end
15
23
  end
16
24
 
17
25
  end
@@ -4,16 +4,31 @@ class ReplaceCommonElementsInFormulae
4
4
  self.new.replace(*args)
5
5
  end
6
6
 
7
+ attr_accessor :common_elements
8
+
7
9
  def replace(input,common,output)
8
- common = common.readlines.map do |a|
10
+ @common_elements ||= {}
11
+ common.readlines.map do |a|
9
12
  ref, element = a.split("\t")
10
- [element.strip,"[:cell, \"#{ref}\"]",ref]
11
- end.sort
13
+ @common_elements[element.strip] = [:cell, ref]
14
+ end
12
15
  input.lines do |line|
13
- common.each do |element,cell,ref|
14
- line.gsub!(element,cell)
15
- end
16
- output.puts line
16
+ ref, formula = line.split("\t")
17
+ output.puts "#{ref}\t#{replace_repeated_formulae(eval(formula)).inspect}"
17
18
  end
18
19
  end
20
+
21
+ def replace_repeated_formulae(ast)
22
+ return ast unless ast.is_a?(Array)
23
+ return ast if [:number,:string,:blank,:null,:error,:boolean_true,:boolean_false,:sheet_reference,:cell, :row].include?(ast.first)
24
+ string = ast.inspect
25
+ return ast if string.length < 20
26
+ if @common_elements.has_key?(string)
27
+ return @common_elements[string]
28
+ end
29
+ ast.map do |a|
30
+ replace_repeated_formulae(a)
31
+ end
32
+ end
33
+
19
34
  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.2
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-17 00:00:00.000000000 Z
12
+ date: 2012-04-24 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rubypeg
16
+ requirement: &70157840000000 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70157840000000
14
25
  - !ruby/object:Gem::Dependency
15
26
  name: nokogiri
16
- requirement: &70281994911220 !ruby/object:Gem::Requirement
27
+ requirement: &70157839999500 !ruby/object:Gem::Requirement
17
28
  none: false
18
29
  requirements:
19
30
  - - ! '>='
@@ -21,10 +32,10 @@ dependencies:
21
32
  version: 1.5.0
22
33
  type: :runtime
23
34
  prerelease: false
24
- version_requirements: *70281994911220
35
+ version_requirements: *70157839999500
25
36
  - !ruby/object:Gem::Dependency
26
37
  name: rspec
27
- requirement: &70281994926800 !ruby/object:Gem::Requirement
38
+ requirement: &70157839999000 !ruby/object:Gem::Requirement
28
39
  none: false
29
40
  requirements:
30
41
  - - ! '>='
@@ -32,13 +43,24 @@ dependencies:
32
43
  version: 2.7.0
33
44
  type: :runtime
34
45
  prerelease: false
35
- version_requirements: *70281994926800
46
+ version_requirements: *70157839999000
47
+ - !ruby/object:Gem::Dependency
48
+ name: ffi
49
+ requirement: &70157839998540 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.11
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70157839998540
36
58
  description: ! "# excel_to_code\n\nConverts some excel spreadsheets (.xlsx, not .xls)
37
59
  into some other programming languages (currently ruby or c).\nThis allows the excel
38
60
  spreadsheets to be run programatically, without excel.\n\nIts cannonical source
39
- is at http://github.com/tamc/excel2code\n\n# Running excel_to_code\n\nTo just have
40
- a go:\n\n\t./bin/excel_to_c <excel_file_name>\n\t\nNB:For small spreadsheets this
41
- will take a minute or so. For large spreadsheets it is best to run it overnight.\n\t\nfor
61
+ is at http://github.com/tamc/excel_to_code\n\n# Running excel_to_code\n\nTo just
62
+ have a go:\n\n\t./bin/excel_to_c <excel_file_name>\n\t\nNB:For small spreadsheets
63
+ this will take a minute or so. For large spreadsheets it is best to run it overnight.\n\t\nfor
42
64
  more detail:\n\t\n\t./bin/excel_to_c --compile --run-tests --settable <name of input
43
65
  worksheet> --prune-except <name of output worksheet> <excel file name> \n\t\nthis
44
66
  should work:\n\n\t./bin/excel_to_c --help\n\n# Testing excel_to_code\n\n1. Make
@@ -56,7 +78,8 @@ executables:
56
78
  extensions: []
57
79
  extra_rdoc_files: []
58
80
  files:
59
- - README
81
+ - README.md
82
+ - TODO
60
83
  - src/commands/excel_to_c.rb
61
84
  - src/commands/excel_to_ruby.rb
62
85
  - src/commands/excel_to_x.rb