excel_to_code 0.0.1 → 0.0.2

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