excel_to_code 0.0.1 → 0.0.2

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/bin/excel_to_c CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'optparse'
3
- require_relative '../src/commands/excel_to_c'
3
+ require_relative '../src/excel_to_code'
4
4
 
5
5
  command = ExcelToC.new
6
6
 
@@ -8,9 +8,10 @@ opts = OptionParser.new do |opts|
8
8
  opts.banner = <<-END
9
9
 
10
10
  A command to approximately translate excel files into c code.
11
+
11
12
  Usage: excel_to_c [options] input_excel_file <output_directory>
12
- input_excel_file is the name of a .xlsx excel file (i.e., produced by Excel 2007+, not an xls file)
13
- output_directory is 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
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
14
15
 
15
16
  Support: http://github.com/tamc/excel2code
16
17
  END
@@ -30,15 +31,18 @@ END
30
31
  command.cells_to_keep = { sheet => :all }
31
32
  end
32
33
 
33
- opts.on('-c','--compile',"Compile the generated c code") do
34
- command.actually_compile_c_code = true
34
+ opts.on('-c','--compile',"Compile the generated code (where relevant)") do
35
+ command.actually_compile_code = true
35
36
  end
36
37
 
37
- opts.on('-r','--run-tests',"Compile the generated c code and then run the tests") do
38
- command.actually_compile_c_code = true
38
+ opts.on('-r','--run-tests',"Compile the generated code and then run the tests") do
39
39
  command.actually_run_tests = true
40
40
  end
41
41
 
42
+ opts.on('-m','--run-in-memory',"Instead of writing intermediate files to disk, uses memory. Requires a lot of memory") do
43
+ command.run_in_memory = true
44
+ end
45
+
42
46
  opts.on("-h", "--help", "Show this message") do
43
47
  puts opts
44
48
  exit
data/src/commands.rb CHANGED
@@ -1,2 +1,3 @@
1
+ require_relative "commands/excel_to_x"
1
2
  require_relative "commands/excel_to_ruby"
2
3
  require_relative "commands/excel_to_c"
@@ -1,521 +1,16 @@
1
1
  # coding: utf-8
2
+ require_relative 'excel_to_x'
2
3
 
3
- require 'fileutils'
4
- require_relative '../util'
5
- require_relative '../excel'
6
- require_relative '../extract'
7
- require_relative '../rewrite'
8
- require_relative '../simplify'
9
- require_relative '../compile'
10
-
11
- # Used to throw normally fatal errors
12
- class ExcelToCException < Exception; end
13
-
14
- class ExcelToC
15
-
16
- # Required attribute. The source excel file. This must be .xlsx not .xls
17
- attr_accessor :excel_file
18
-
19
- # Optional attribute. The output directory.
20
- # If not specified, will be '#{excel_file_name}/c'
21
- attr_accessor :output_directory
22
-
23
- # Optional attribute. The name of the resulting c file (and associated ruby ffi module). Defaults to excelspreadsheet
24
- attr_accessor :output_name
25
-
26
- # Optional attribute. The excel file will be translated to xml and stored here.
27
- # If not specified, will be '#{excel_file_name}/xml'
28
- attr_accessor :xml_directory
29
-
30
- # Optional attribute. The intermediate workings will be stored here.
31
- # If not specified, will be '#{excel_file_name}/intermediate'
32
- attr_accessor :intermediate_directory
33
-
34
- # Optional attribute. Specifies which cells have setters created in the c code so their values can be altered at runtime.
35
- # 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
36
- # should be setable, or an array of cell names on that sheet that should be settable (e.g., A1)
37
- attr_accessor :cells_that_can_be_set_at_runtime
38
-
39
- # Optional attribute. Specifies which cells must appear in the final generated code.
40
- # The default is that all cells in the original spreadsheet appear in the final code.
41
- #
42
- # If specified, then any cells that are not:
43
- # * specified
44
- # * required to calculate one of the cells that is specified
45
- # * specified as a cell that can be set at runtime
46
- # may be excluded from the final generated code.
47
- #
48
- # 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
49
- # should be lept, or an array of cell names on that sheet that should be kept (e.g., A1)
50
- attr_accessor :cells_to_keep
51
-
52
- # Optional attribute. Boolean.
53
- # * true - the generated c code is compiled
54
- # * false - the generated c code is not compiled (default, unless actuall_run_tests is specified as true)
55
- attr_accessor :actually_compile_c_code
56
-
57
- # Optional attribute. Boolean.
58
- # * true - the generated tests are run
59
- # * false - the generated tests are not run
60
- attr_accessor :actually_run_tests
61
-
62
- def set_defaults
63
- raise ExcelToCException.new("No excel file has been specified in ExcelToC.excel_file") unless excel_file
64
-
65
- self.output_directory ||= File.join(File.dirname(excel_file),File.basename(excel_file,".*"),'c')
66
- self.xml_directory ||= File.join(File.dirname(excel_file),File.basename(excel_file,".*"),'xml')
67
- self.intermediate_directory ||= File.join(File.dirname(excel_file),File.basename(excel_file,".*"),'intermediate')
68
-
69
- self.output_name ||= "Excelspreadsheet"
70
-
71
- self.cells_that_can_be_set_at_runtime ||= {}
72
-
73
- # Make sure that all the cell names are downcase and don't have any $ in them
74
- cells_that_can_be_set_at_runtime.keys.each do |sheet|
75
- next unless cells_that_can_be_set_at_runtime[sheet].is_a?(Array)
76
- cells_that_can_be_set_at_runtime[sheet] = cells_that_can_be_set_at_runtime[sheet].map { |reference| reference.gsub('$','').downcase }
77
- end
78
-
79
- if cells_to_keep
80
- cells_to_keep.keys.each do |sheet|
81
- next unless cells_to_keep[sheet].is_a?(Array)
82
- cells_to_keep[sheet] = cells_to_keep[sheet].map { |reference| reference.gsub('$','').downcase }
83
- end
84
- end
85
-
86
- self.excel_file = File.expand_path(excel_file)
87
- self.output_directory = File.expand_path(output_directory)
88
-
89
- end
4
+ class ExcelToC < ExcelToX
90
5
 
91
- def go!
92
- # This sorts out the attributes
93
- set_defaults
94
-
95
- # These turn the excel into a more accesible format
96
- sort_out_output_directories
97
- unzip_excel
98
-
99
- # These get all the information out of the excel and put
100
- # into a useful format
101
- extract_data_from_workbook
102
- extract_data_from_worksheets
103
- merge_table_files
104
- rewrite_worksheets
105
-
106
- # These perform a series of transformations to the information
107
- # with the intent of removing any redundant calculations
108
- # that are in the excel
109
- simplify_worksheets
110
- optimise_and_replace_indirect_loop
111
- replace_blanks
112
- remove_any_cells_not_needed_for_outputs
113
- inline_formulae_that_are_only_used_once
114
- separate_formulae_elements
115
-
116
- # These actually create the code version of the excel
6
+ # These actually create the code version of the excel
7
+ def write_code
117
8
  write_out_excel_as_code
118
9
  write_build_script
119
10
  write_fuby_ffi_interface
120
11
  write_tests
121
-
122
- # These compile and run the code version of the excel
123
- compile_c_code
124
- run_tests
125
-
126
- puts
127
- puts "The generated code is available in #{File.join(output_directory)}"
128
- end
129
-
130
- def sort_out_output_directories
131
- FileUtils.mkdir_p(output_directory)
132
- FileUtils.mkdir_p(xml_directory)
133
- FileUtils.mkdir_p(intermediate_directory)
134
- end
135
-
136
- def unzip_excel
137
- puts `rm -fr '#{xml_directory}'`
138
- puts `unzip -uo '#{excel_file}' -d '#{xml_directory}'`
139
- end
140
-
141
- def extract_data_from_workbook
142
- extract_shared_strings
143
-
144
- extract ExtractNamedReferences, 'workbook.xml', 'named_references'
145
- rewrite RewriteFormulaeToAst, 'named_references', 'named_references.ast'
146
-
147
- extract ExtractWorksheetNames, 'workbook.xml', 'worksheet_names_without_filenames'
148
- extract ExtractRelationships, File.join('_rels','workbook.xml.rels'), 'workbook_relationships'
149
- rewrite RewriteWorksheetNames, 'worksheet_names_without_filenames', 'workbook_relationships', 'worksheet_names'
150
- rewrite MapSheetNamesToCNames, 'worksheet_names', 'worksheet_c_names'
151
-
152
- extract_dimensions_from_worksheets
153
- end
154
-
155
- def extract_shared_strings
156
- if File.exists?(File.join(xml_directory,'xl','sharedStrings.xml'))
157
- extract ExtractSharedStrings, 'sharedStrings.xml', 'shared_strings'
158
- else
159
- FileUtils.touch(File.join(intermediate_directory,'shared_strings'))
160
- end
161
- end
162
-
163
- def extract_dimensions_from_worksheets
164
- dimension_file = intermediate('dimensions')
165
- worksheets("Extracting dimensions") do |name,xml_filename|
166
- dimension_file.write name
167
- dimension_file.write "\t"
168
- extract ExtractWorksheetDimensions, File.open(xml_filename,'r'), dimension_file
169
- end
170
- dimension_file.close
171
12
  end
172
-
173
- def extract_data_from_worksheets
174
- worksheets("Initial data extract") do |name,xml_filename|
175
- worksheet_directory = File.join(intermediate_directory,name)
176
- FileUtils.mkdir_p(worksheet_directory)
177
- worksheet_xml = File.open(xml_filename,'r')
178
- { ExtractValues => 'values',
179
- ExtractSimpleFormulae => 'simple_formulae',
180
- ExtractSharedFormulae => 'shared_formulae',
181
- ExtractArrayFormulae => 'array_formulae'
182
- }.each do |_klass,output_filename|
183
- worksheet_xml.rewind
184
- extract _klass, worksheet_xml, File.join(name,output_filename)
185
- if _klass == ExtractValues
186
- rewrite RewriteValuesToAst, File.join(name,output_filename), File.join(name,"#{output_filename}.ast")
187
- else
188
- rewrite RewriteFormulaeToAst, File.join(name,output_filename), File.join(name,"#{output_filename}.ast")
189
- end
190
- end
191
- worksheet_xml.rewind
192
- extract ExtractWorksheetTableRelationships, worksheet_xml, File.join(name,'table_rids')
193
- if File.exists?(File.join(xml_directory,'xl','worksheets','_rels',"#{File.basename(xml_filename)}.rels"))
194
- extract ExtractRelationships, File.join('worksheets','_rels',"#{File.basename(xml_filename)}.rels"), File.join(name,'relationships')
195
- rewrite RewriteRelationshipIdToFilename, File.join(name,'table_rids'), File.join(name,'relationships'), File.join(name,'table_filenames')
196
- tables = intermediate(name,'tables')
197
- table_extractor = ExtractTable.new(name)
198
- table_filenames = input(name,'table_filenames')
199
- table_filenames.lines.each do |line|
200
- extract table_extractor, File.join('worksheets',line.strip), tables
201
- end
202
- close(tables,table_filenames)
203
- else
204
- FileUtils.touch File.join(intermediate_directory,name,'relationships')
205
- FileUtils.touch File.join(intermediate_directory,name,'table_filenames')
206
- FileUtils.touch File.join(intermediate_directory,name,'tables')
207
- end
208
- close(worksheet_xml)
209
- end
210
- end
211
-
212
- def rewrite_worksheets
213
- worksheets("Initial rewrite of references and formulae") do |name,xml_filename|
214
- rewrite_row_and_column_references(name,xml_filename)
215
- rewrite_shared_formulae(name,xml_filename)
216
- rewrite_array_formulae(name,xml_filename)
217
- combine_formulae_files(name,xml_filename)
218
- end
219
- end
220
-
221
- def rewrite_row_and_column_references(name,xml_filename)
222
- dimensions = input('dimensions')
223
- %w{simple_formulae.ast shared_formulae.ast array_formulae.ast}.each do |file|
224
- dimensions.rewind
225
- i = File.open(File.join(intermediate_directory,name,file),'r')
226
- o = File.open(File.join(intermediate_directory,name,"#{file}-nocols"),'w')
227
- RewriteWholeRowColumnReferencesToAreas.rewrite(i,name, dimensions, o)
228
- close(i,o)
229
- end
230
- dimensions.close
231
- end
232
-
233
- def rewrite_shared_formulae(name,xml_filename)
234
- i = File.open(File.join(intermediate_directory,name,'shared_formulae.ast-nocols'),'r')
235
- o = File.open(File.join(intermediate_directory,name,"shared_formulae-expanded.ast"),'w')
236
- RewriteSharedFormulae.rewrite(i,o)
237
- close(i,o)
238
- end
239
-
240
- def rewrite_array_formulae(name,xml_filename)
241
- r = ReplaceNamedReferences.new
242
- r.sheet_name = name
243
- replace r, File.join(name,'array_formulae.ast-nocols'), 'named_references.ast', File.join(name,"array_formulae1.ast")
244
-
245
- r = ReplaceTableReferences.new
246
- r.sheet_name = name
247
- replace r, File.join(name,'array_formulae1.ast'), 'all_tables', File.join(name,"array_formulae2.ast")
248
- replace SimplifyArithmetic, File.join(name,'array_formulae2.ast'), File.join(name,'array_formulae3.ast')
249
- replace ReplaceRangesWithArrayLiterals, File.join(name,"array_formulae3.ast"), File.join(name,"array_formulae4.ast")
250
- rewrite RewriteArrayFormulaeToArrays, File.join(name,"array_formulae4.ast"), File.join(name,"array_formulae5.ast")
251
- rewrite RewriteArrayFormulae, File.join(name,'array_formulae5.ast'), File.join(name,"array_formulae-expanded.ast")
252
- end
253
-
254
- def combine_formulae_files(name,xml_filename)
255
- values = File.join(name,'values.ast')
256
- shared_formulae = File.join(name,"shared_formulae-expanded.ast")
257
- array_formulae = File.join(name,"array_formulae-expanded.ast")
258
- simple_formulae = File.join(name,"simple_formulae.ast-nocols")
259
- output = File.join(name,'formulae.ast')
260
- rewrite RewriteMergeFormulaeAndValues, values, shared_formulae, array_formulae, simple_formulae, output
261
- end
262
-
263
- def merge_table_files
264
- tables = []
265
- worksheets("Merging table files") do |name,xml_filename|
266
- tables << File.join(intermediate_directory,name,'tables')
267
- end
268
- `sort #{tables.map { |t| " '#{t}' "}.join} > #{File.join(intermediate_directory,'all_tables')}`
269
- end
270
-
271
- def simplify_worksheets
272
- worksheets("Simplifying") do |name,xml_filename|
273
- simplify_worksheet(name,xml_filename)
274
- end
275
- end
276
-
277
- def simplify_worksheet(name,xml_filename)
278
- replace SimplifyArithmetic, File.join(name,'formulae.ast'), File.join(name,'formulae_simple_arithmetic.ast')
279
- replace ReplaceSharedStrings, File.join(name,'formulae_simple_arithmetic.ast'), 'shared_strings', File.join(name,"formulae_no_shared_strings.ast")
280
- replace ReplaceSharedStrings, File.join(name,'values.ast'), 'shared_strings', File.join(name,"values_no_shared_strings.ast")
281
- r = ReplaceNamedReferences.new
282
- r.sheet_name = name
283
- replace r, File.join(name,'formulae_no_shared_strings.ast'), 'named_references.ast', File.join(name,"formulae_no_named_references.ast")
284
-
285
- r = ReplaceTableReferences.new
286
- r.sheet_name = name
287
- replace r, File.join(name,'formulae_no_named_references.ast'), 'all_tables', File.join(name,"formulae_no_table_references.ast")
288
- replace ReplaceRangesWithArrayLiterals, File.join(name,"formulae_no_table_references.ast"), File.join(name,"formulae_no_ranges.ast")
289
- end
290
-
291
- def replace_blanks
292
- references = {}
293
- worksheets("Loading formulae") do |name,xml_filename|
294
- r = references[name] = {}
295
- i = input(name,"formulae_no_indirects_optimised.ast")
296
- i.lines do |line|
297
- ref = line[/^(.*?)\t/,1]
298
- r[ref] = true
299
- end
300
- end
301
- worksheets("Replacing blanks") do |name,xml_filename|
302
- #fork do
303
- r = ReplaceBlanks.new
304
- r.references = references
305
- r.default_sheet_name = name
306
- replace r, File.join(name,"formulae_no_indirects_optimised.ast"),File.join(name,"formulae_no_blanks.ast")
307
- #end
308
- end
309
- end
310
-
311
- def optimise_and_replace_indirect_loop
312
- number_of_loops = 4
313
- 1.upto(number_of_loops) do |pass|
314
- puts "Optimise and replace indirects pass #{pass}"
315
- start = pass == 1 ? "formulae_no_ranges.ast" : "optimse-output-#{pass-1}.ast"
316
- finish = pass == number_of_loops ? "formulae_no_indirects_optimised.ast" : "optimse-output-#{pass}.ast"
317
- replace_indirects(start,"replace-indirect-output-#{pass}.ast","replace-indirect-working-#{pass}-")
318
- optimise_sheets("replace-indirect-output-#{pass}.ast",finish,"optimse-working-#{pass}-")
319
- end
320
- end
321
-
322
- def replace_indirects(start_filename,finish_filename,basename)
323
- worksheets("Replacing indirects") do |name,xml_filename|
324
- counter = 1
325
- replace ReplaceIndirectsWithReferences, File.join(name,start_filename), File.join(name,"#{basename}#{counter+1}.ast")
326
- counter += 1
327
-
328
- r = ReplaceNamedReferences.new
329
- r.sheet_name = name
330
- replace r, File.join(name,"#{basename}#{counter}.ast"), 'named_references.ast', File.join(name,"#{basename}#{counter+1}.ast")
331
- counter += 1
332
-
333
- r = ReplaceTableReferences.new
334
- r.sheet_name = name
335
- replace r, File.join(name,"#{basename}#{counter}.ast"), 'all_tables', File.join(name,"#{basename}#{counter+1}.ast")
336
- counter += 1
337
-
338
- replace ReplaceRangesWithArrayLiterals, File.join(name,"#{basename}#{counter}.ast"), File.join(name,"#{basename}#{counter+1}.ast")
339
- counter += 1
340
- replace ReplaceArraysWithSingleCells, File.join(name,"#{basename}#{counter}.ast"), File.join(name,"#{basename}#{counter+1}.ast")
341
- counter += 1
342
-
343
- # Finally, create the output directory
344
- i = File.join(intermediate_directory,name,"#{basename}#{counter}.ast")
345
- o = File.join(intermediate_directory,name,finish_filename)
346
- `cp '#{i}' '#{o}'`
347
- end
348
- end
349
-
350
- def optimise_sheets(start_filename,finish_filename,basename)
351
- counter = 1
352
13
 
353
- # Setup start
354
- worksheets("Setting up for optimise -#{counter}") do |name|
355
- i = File.join(intermediate_directory,name,start_filename)
356
- o = File.join(intermediate_directory,name,"#{basename}#{counter}.ast")
357
- `cp '#{i}' '#{o}'`
358
- end
359
-
360
- worksheets("Replacing with calculated values #{counter}-#{counter+1}") do |name,xml_filename|
361
- #fork do
362
- replace ReplaceFormulaeWithCalculatedValues, File.join(name,"#{basename}#{counter}.ast"), File.join(name,"#{basename}#{counter+1}.ast")
363
- #end
364
- end
365
- counter += 1
366
- Process.waitall
367
-
368
- references = all_formulae("#{basename}#{counter}.ast")
369
- inline_ast_decision = lambda do |sheet,cell,references|
370
- references_to_keep = @cells_that_can_be_set_at_runtime[sheet]
371
- if references_to_keep && (references_to_keep == :all || references_to_keep.include?(cell))
372
- false
373
- else
374
- ast = references[sheet][cell]
375
- if ast
376
- if [:number,:string,:blank,:null,:error,:boolean_true,:boolean_false,:sheet_reference,:cell].include?(ast.first)
377
- # puts "Inlining #{sheet}.#{cell}: #{ast.inspect}"
378
- true
379
- else
380
- false
381
- end
382
- else
383
- true # Always inline blanks
384
- end
385
- end
386
- end
387
- r = InlineFormulae.new
388
- r.references = references
389
- r.inline_ast = inline_ast_decision
390
-
391
- worksheets("Inlining formulae #{counter}-#{counter+1}") do |name,xml_filename|
392
- #fork do
393
- r.default_sheet_name = name
394
- replace r, File.join(name,"#{basename}#{counter}.ast"), File.join(name,"#{basename}#{counter+1}.ast")
395
- #end
396
- end
397
- counter += 1
398
- Process.waitall
399
-
400
- # Finish
401
- worksheets("Moving sheets #{counter}-") do |name|
402
- o = File.join(intermediate_directory,name,finish_filename)
403
- i = File.join(intermediate_directory,name,"#{basename}#{counter}.ast")
404
- `cp '#{i}' '#{o}'`
405
- end
406
- end
407
-
408
- def remove_any_cells_not_needed_for_outputs(formula_in = "formulae_no_blanks.ast", formula_out = "formulae_pruned.ast", values_in = "values_no_shared_strings.ast", values_out = "values_pruned.ast")
409
- if cells_to_keep && !cells_to_keep.empty?
410
- identifier = IdentifyDependencies.new
411
- identifier.references = all_formulae(formula_in)
412
- cells_to_keep.each do |sheet_to_keep,cells_to_keep|
413
- if cells_to_keep == :all
414
- identifier.add_depedencies_for(sheet_to_keep)
415
- elsif cells_to_keep.is_a?(Array)
416
- cells_to_keep.each do |cell|
417
- identifier.add_depedencies_for(sheet_to_keep,cell)
418
- end
419
- end
420
- end
421
- r = RemoveCells.new
422
- worksheets("Removing cells") do |name,xml_filename|
423
- #fork do
424
- r.cells_to_keep = identifier.dependencies[name]
425
- rewrite r, File.join(name, formula_in), File.join(name, formula_out)
426
- rewrite r, File.join(name, values_in), File.join(name, values_out)
427
- #end
428
- end
429
- Process.waitall
430
- else
431
- worksheets do |name,xml_filename|
432
- i = File.join(intermediate_directory,name, formula_in)
433
- o = File.join(intermediate_directory,name, formula_out)
434
- `cp '#{i}' '#{o}'`
435
- i = File.join(intermediate_directory,name, values_in)
436
- o = File.join(intermediate_directory,name, values_out)
437
- `cp '#{i}' '#{o}'`
438
- end
439
- end
440
- end
441
-
442
- def inline_formulae_that_are_only_used_once
443
- references = all_formulae("formulae_pruned.ast")
444
- counter = CountFormulaReferences.new
445
- count = counter.count(references)
446
-
447
- inline_ast_decision = lambda do |sheet,cell,references|
448
- references_to_keep = @cells_that_can_be_set_at_runtime[sheet]
449
- if references_to_keep && (references_to_keep == :all || references_to_keep.include?(cell))
450
- false
451
- else
452
- count[sheet][cell] == 1
453
- end
454
- end
455
-
456
- r = InlineFormulae.new
457
- r.references = references
458
- r.inline_ast = inline_ast_decision
459
-
460
- worksheets("Inlining formulae") do |name,xml_filename|
461
- #fork do
462
- r.default_sheet_name = name
463
- replace r, File.join(name,"formulae_pruned.ast"), File.join(name,"formulae_inlined.ast")
464
- #end
465
- end
466
-
467
- remove_any_cells_not_needed_for_outputs("formulae_inlined.ast", "formulae_inlined_pruned.ast", "values_pruned.ast", "values_pruned2.ast")
468
-
469
- end
470
-
471
- def separate_formulae_elements
472
- # First we add the sheet to all references, so that we can then look for common elements accross worksheets
473
- r = RewriteCellReferencesToIncludeSheet.new
474
- worksheets("Adding the sheet to all references") do |name,xml_filename|
475
- r.worksheet = name
476
- rewrite r, File.join(name,"formulae_inlined_pruned.ast"), File.join(name,"formulae_inlined_pruned_with_sheets.ast")
477
- end
478
-
479
- references = all_formulae("formulae_inlined_pruned_with_sheets.ast")
480
- identifier = IdentifyRepeatedFormulaElements.new
481
- repeated_elements = identifier.count(references)
482
- repeated_elements.delete_if do |element,count|
483
- count < 2
484
- end
485
- o = intermediate('common-elements-1.ast')
486
- i = 0
487
- repeated_elements.each do |element,count|
488
- o.puts "common#{i}\t#{element}"
489
- i = i + 1
490
- end
491
- close(o)
492
-
493
- worksheets("Replacing repeated elements") do |name,xml_filename|
494
- replace ReplaceCommonElementsInFormulae, File.join(name,"formulae_inlined_pruned_with_sheets.ast"), "common-elements-1.ast", File.join(name,"formulae_inlined_pruned_replaced-1.ast")
495
- end
496
-
497
- r = ReplaceValuesWithConstants.new
498
- worksheets("Replacing values with constants") do |name,xml_filename|
499
- i = input(name,"formulae_inlined_pruned_replaced-1.ast")
500
- o = intermediate(name,"formulae_inlined_pruned_replaced.ast")
501
- r.replace(i,o)
502
- close(i,o)
503
- end
504
-
505
- puts "Replacing values with constants in common elements"
506
- i = input("common-elements-1.ast")
507
- o = intermediate("common-elements.ast")
508
- r.replace(i,o)
509
- close(i,o)
510
-
511
- puts "Writing out constants"
512
- co = intermediate("value_constants.ast")
513
- r.rewriter.constants.each do |ast,constant|
514
- co.puts "#{constant}\t#{ast}"
515
- end
516
- close(co)
517
- end
518
-
519
14
  def write_out_excel_as_code
520
15
 
521
16
  all_refs = all_formulae("formulae_inlined_pruned_replaced.ast")
@@ -623,28 +118,6 @@ class ExcelToC
623
118
  end
624
119
  close(w,o)
625
120
  end
626
-
627
- def settable(name)
628
- settable_refs = @cells_that_can_be_set_at_runtime[name]
629
- if settable_refs
630
- lambda { |ref| (settable_refs == :all) ? true : settable_refs.include?(ref) }
631
- else
632
- lambda { |ref| false }
633
- end
634
- end
635
-
636
- def gettable(name)
637
- if @cells_to_keep
638
- gettable_refs = @cells_to_keep[name]
639
- if gettable_refs
640
- lambda { |ref| (gettable_refs == :all) ? true : gettable_refs.include?(ref) }
641
- else
642
- lambda { |ref| false }
643
- end
644
- else
645
- lambda { |ref| true }
646
- end
647
- end
648
121
 
649
122
  def write_build_script
650
123
  o = output("Makefile")
@@ -762,8 +235,8 @@ END
762
235
  close(o)
763
236
  end
764
237
 
765
- def compile_c_code
766
- return unless actually_compile_c_code || actually_run_tests
238
+ def compile_code
239
+ return unless actually_compile_code || actually_run_tests
767
240
  puts "Compiling the resulting c code"
768
241
  puts `cd #{File.join(output_directory)}; make clean; make`
769
242
  end
@@ -774,85 +247,4 @@ END
774
247
  puts `cd #{File.join(output_directory)}; ruby "#{output_name.downcase}_test.rb"`
775
248
  end
776
249
 
777
-
778
- # UTILITY FUNCTIONS
779
-
780
- def all_formulae(filename)
781
- references = {}
782
- worksheets do |name,xml_filename|
783
- r = references[name] = {}
784
- i = input(name,filename)
785
- i.lines do |line|
786
- line =~ /^(.*?)\t(.*)$/
787
- ref, ast = $1, $2
788
- r[$1] = eval($2)
789
- end
790
- end
791
- references
792
- end
793
-
794
- def c_name_for_worksheet_name(name)
795
- unless @worksheet_names
796
- w = input("worksheet_c_names")
797
- @worksheet_names = Hash[w.readlines.map { |line| line.split("\t").map { |a| a.strip }}]
798
- close(w)
799
- end
800
- @worksheet_names[name]
801
- end
802
-
803
- def worksheets(message = "Processing",&block)
804
- IO.readlines(File.join(intermediate_directory,'worksheet_names')).each do |line|
805
- name, filename = *line.split("\t")
806
- filename = File.expand_path(File.join(xml_directory,'xl',filename.strip))
807
- puts "#{message} #{name}"
808
- block.call(name, filename)
809
- end
810
- end
811
-
812
- def extract(_klass,xml_name,output_name)
813
- i = xml_name.is_a?(String) ? xml(xml_name) : xml_name
814
- o = output_name.is_a?(String) ? intermediate(output_name) : output_name
815
- _klass.extract(i,o)
816
- if xml_name.is_a?(String)
817
- close(i)
818
- end
819
- if output_name.is_a?(String)
820
- close(o)
821
- end
822
- end
823
-
824
- def rewrite(_klass,*args)
825
- o = intermediate(args.pop)
826
- inputs = args.map { |name| input(name) }
827
- _klass.rewrite(*inputs,o)
828
- close(*inputs,o)
829
- end
830
-
831
- def replace(_klass,*args)
832
- o = intermediate(args.pop)
833
- inputs = args.map { |name| input(name) }
834
- _klass.replace(*inputs,o)
835
- close(*inputs,o)
836
- end
837
-
838
- def xml(*args)
839
- File.open(File.join(xml_directory,'xl',*args),'r')
840
- end
841
-
842
- def input(*args)
843
- File.open(File.join(intermediate_directory,*args),'r')
844
- end
845
-
846
- def intermediate(*args)
847
- File.open(File.join(intermediate_directory,*args),'w')
848
- end
849
-
850
- def output(*args)
851
- File.open(File.join(output_directory,*args),'w')
852
- end
853
-
854
- def close(*args)
855
- args.map(&:close)
856
- end
857
-
858
- end
250
+ end