excel_to_code 0.1.8 → 0.1.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/TODO +0 -1
- data/src/commands/excel_to_c.rb +6 -5
- data/src/commands/excel_to_ruby.rb +2 -1
- data/src/commands/excel_to_x.rb +69 -37
- data/src/compile/c/a.out +0 -0
- data/src/compile/c/compile_named_reference_setters.rb +1 -1
- data/src/compile/c/compile_to_c.rb +1 -1
- data/src/compile/c/compile_to_c_header.rb +1 -1
- data/src/compile/c/excel_to_c_runtime.c +117 -1
- data/src/compile/c/map_formulae_to_c.rb +4 -0
- data/src/compile/c/map_sheet_names_to_c_names.rb +1 -1
- data/src/compile/c/map_values_to_c.rb +2 -1
- data/src/compile/ruby/compile_to_ruby.rb +2 -2
- data/src/compile/ruby/compile_to_ruby_unit_test.rb +1 -1
- data/src/compile/ruby/map_formulae_to_ruby.rb +5 -0
- data/src/compile/ruby/map_values_to_ruby.rb +2 -1
- data/src/excel/excel_functions.rb +10 -0
- data/src/excel/excel_functions/cell.rb +14 -0
- data/src/excel/excel_functions/mid.rb +20 -0
- data/src/excel/excel_functions/negative.rb +2 -0
- data/src/excel/excel_functions/pv.rb +37 -0
- data/src/excel/excel_functions/text.rb +25 -0
- data/src/excel/excel_functions/trim.rb +8 -0
- data/src/excel/table.rb +1 -0
- data/src/extract/check_for_unknown_functions.rb +1 -1
- data/src/rewrite/ast_expand_array_formulae.rb +4 -6
- data/src/rewrite/rewrite_array_formulae.rb +2 -2
- data/src/rewrite/rewrite_array_formulae_to_arrays.rb +1 -1
- data/src/rewrite/rewrite_cell_references_to_include_sheet.rb +1 -1
- data/src/rewrite/rewrite_formulae_to_ast.rb +1 -1
- data/src/rewrite/rewrite_merge_formulae_and_values.rb +2 -2
- data/src/rewrite/rewrite_named_reference_names.rb +1 -1
- data/src/rewrite/rewrite_relationship_id_to_filename.rb +1 -1
- data/src/rewrite/rewrite_shared_formulae.rb +2 -2
- data/src/rewrite/rewrite_values_to_ast.rb +1 -1
- data/src/rewrite/rewrite_whole_row_column_references_to_areas.rb +2 -2
- data/src/rewrite/rewrite_worksheet_names.rb +6 -3
- data/src/simplify.rb +2 -0
- data/src/simplify/inline_formulae.rb +18 -1
- data/src/simplify/map_formulae_to_values.rb +18 -6
- data/src/simplify/remove_cells.rb +1 -1
- data/src/simplify/replace_arrays_with_single_cells.rb +1 -1
- data/src/simplify/replace_column_with_column_number.rb +58 -0
- data/src/simplify/replace_common_elements_in_formulae.rb +1 -1
- data/src/simplify/replace_formulae_with_calculated_values.rb +7 -1
- data/src/simplify/replace_indirects_with_references.rb +16 -3
- data/src/simplify/replace_named_references.rb +1 -1
- data/src/simplify/replace_offsets_with_references.rb +66 -0
- data/src/simplify/replace_ranges_with_array_literals.rb +1 -1
- data/src/simplify/replace_shared_strings.rb +1 -1
- data/src/simplify/replace_table_references.rb +2 -2
- data/src/simplify/replace_values_with_constants.rb +1 -1
- data/src/simplify/simplify_arithmetic.rb +1 -1
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4e983a098e0ba3377ae5b0d42b53c5d627e765e8
|
4
|
+
data.tar.gz: dd9b3766572dd84f5bf980fb256bfd7ce70fdb49
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b829c3fb99bd61216b7df001ad320a9a0ecd6f6157fc07ac0c368417354e853544f25ae8b31b43dde6fb788480c6ac9e8559b6646fd8a5d55bd4a054885c0e62
|
7
|
+
data.tar.gz: 6d1f1519d8daf601c6028c6c116eadd8c76dada8edb5153ee42d49946fa97831b18d0d086e46a59b49823c6126e893638f0247c2e30b2de08a3f053a917bc49a
|
data/TODO
CHANGED
@@ -19,7 +19,6 @@ See doc/How_to_add_a_missing_function.md
|
|
19
19
|
|
20
20
|
* Optimize IF, CHOOSE, MATCH, VLOOKUP and similar functions so that they don't have to calculate all their arguments
|
21
21
|
* Fix it so that cells that are being reported as empty, that excel would give a numeric value of zero, are fixed
|
22
|
-
* Fix so that detects when it has finished replacing cells with values, rather than just doing a fixed number of cycles
|
23
22
|
|
24
23
|
## Things that are badly written
|
25
24
|
|
data/src/commands/excel_to_c.rb
CHANGED
@@ -36,13 +36,14 @@ class ExcelToC < ExcelToX
|
|
36
36
|
|
37
37
|
# Now we have to put all the initial definitions out
|
38
38
|
o.puts "// definitions"
|
39
|
+
o.puts "static ExcelValue ORIGINAL_EXCEL_FILENAME = {.type = ExcelString, .string = #{excel_file.inspect} };"
|
39
40
|
|
40
41
|
i = input("Common elements")
|
41
42
|
c = CompileToCHeader.new
|
42
43
|
c.gettable = lambda { |ref| false }
|
43
44
|
c.rewrite(i,w,o)
|
44
45
|
i.rewind
|
45
|
-
number_of_refs += i.
|
46
|
+
number_of_refs += i.each_line.to_a.size
|
46
47
|
close(i)
|
47
48
|
|
48
49
|
worksheets do |name,xml_filename|
|
@@ -54,7 +55,7 @@ class ExcelToC < ExcelToX
|
|
54
55
|
i = input([name,"Formulae"])
|
55
56
|
c.rewrite(i,w,o)
|
56
57
|
i.rewind
|
57
|
-
number_of_refs += i.
|
58
|
+
number_of_refs += i.each_line.to_a.size
|
58
59
|
close(i)
|
59
60
|
end
|
60
61
|
|
@@ -82,7 +83,7 @@ class ExcelToC < ExcelToX
|
|
82
83
|
o.puts "// starting the value constants"
|
83
84
|
mapper = MapValuesToCStructs.new
|
84
85
|
i = input("Constants")
|
85
|
-
i.
|
86
|
+
i.each_line do |line|
|
86
87
|
begin
|
87
88
|
ref, formula = line.split("\t")
|
88
89
|
ast = eval(formula)
|
@@ -344,7 +345,7 @@ END
|
|
344
345
|
|
345
346
|
# Getters
|
346
347
|
i = input('Named references to keep')
|
347
|
-
i.
|
348
|
+
i.each_line do |line|
|
348
349
|
name = line.strip.split("\t").first
|
349
350
|
o.puts " attach_function '#{name}', [], ExcelValue.by_value"
|
350
351
|
end
|
@@ -352,7 +353,7 @@ END
|
|
352
353
|
|
353
354
|
# Setters
|
354
355
|
i = input('Named references to set')
|
355
|
-
i.
|
356
|
+
i.each_line do |line|
|
356
357
|
name = line.strip.split("\t").first
|
357
358
|
o.puts " attach_function 'set_#{name}', [ExcelValue.by_value], :void"
|
358
359
|
end
|
@@ -30,6 +30,7 @@ class ExcelToRuby < ExcelToX
|
|
30
30
|
o.puts ""
|
31
31
|
o.puts "class #{ruby_module_name}"
|
32
32
|
o.puts " include ExcelFunctions"
|
33
|
+
o.puts " def original_excel_filename; #{excel_file.inspect}; end"
|
33
34
|
|
34
35
|
o.puts
|
35
36
|
o.puts " # Starting common elements"
|
@@ -66,7 +67,7 @@ class ExcelToRuby < ExcelToX
|
|
66
67
|
o.puts " # starting initializer"
|
67
68
|
o.puts " def initialize"
|
68
69
|
d = input('Defaults')
|
69
|
-
d.
|
70
|
+
d.each_line do |line|
|
70
71
|
o.puts line
|
71
72
|
end
|
72
73
|
o.puts " end"
|
data/src/commands/excel_to_x.rb
CHANGED
@@ -110,9 +110,11 @@ class ExcelToX
|
|
110
110
|
self.cells_that_can_be_set_at_runtime ||= {}
|
111
111
|
|
112
112
|
# Make sure that all the cell names are downcase and don't have any $ in them
|
113
|
-
cells_that_can_be_set_at_runtime.
|
114
|
-
|
115
|
-
|
113
|
+
if cells_that_can_be_set_at_runtime.is_a?(Hash)
|
114
|
+
cells_that_can_be_set_at_runtime.keys.each do |sheet|
|
115
|
+
next unless cells_that_can_be_set_at_runtime[sheet].is_a?(Array)
|
116
|
+
cells_that_can_be_set_at_runtime[sheet] = cells_that_can_be_set_at_runtime[sheet].map { |reference| reference.gsub('$','').upcase }
|
117
|
+
end
|
116
118
|
end
|
117
119
|
|
118
120
|
# Make sure that all the cell names are downcase and don't have any $ in them
|
@@ -168,9 +170,9 @@ class ExcelToX
|
|
168
170
|
simplify_worksheets # Replacing shared strings and named references with their actual values, tidying arithmetic
|
169
171
|
|
170
172
|
# In case this hasn't been set by the user
|
171
|
-
if cells_that_can_be_set_at_runtime.empty?
|
173
|
+
if @cells_that_can_be_set_at_runtime.empty?
|
172
174
|
log.info "Creating a good set of cells that should be settable"
|
173
|
-
|
175
|
+
@cells_that_can_be_set_at_runtime = a_good_set_of_cells_that_should_be_settable_at_runtime
|
174
176
|
end
|
175
177
|
|
176
178
|
if named_references_that_can_be_set_at_runtime == :where_possible
|
@@ -313,7 +315,7 @@ class ExcelToX
|
|
313
315
|
table_filenames = input(name, "Worksheet tables")
|
314
316
|
tables = intermediate(name, "Worksheet tables")
|
315
317
|
table_extractor = ExtractTable.new(name)
|
316
|
-
table_filenames.
|
318
|
+
table_filenames.each_line do |line|
|
317
319
|
table_xml = xml(File.join('worksheets',line.strip))
|
318
320
|
table_extractor.extract(table_xml, tables)
|
319
321
|
end
|
@@ -328,7 +330,7 @@ class ExcelToX
|
|
328
330
|
worksheets do |name,xml_filename|
|
329
331
|
log.info "Merging table files for #{name}"
|
330
332
|
worksheet_table_file = input([name, "Worksheet tables"])
|
331
|
-
worksheet_table_file.
|
333
|
+
worksheet_table_file.each_line do |line|
|
332
334
|
merged_table_file.puts line
|
333
335
|
end
|
334
336
|
close worksheet_table_file
|
@@ -477,6 +479,9 @@ class ExcelToX
|
|
477
479
|
def work_out_which_named_references_can_be_set_at_runtime
|
478
480
|
return unless @named_references_that_can_be_set_at_runtime
|
479
481
|
return unless @named_references_that_can_be_set_at_runtime == :where_possible
|
482
|
+
cells_that_can_be_set = @cells_that_can_be_set_at_runtime
|
483
|
+
cells_that_can_be_set = a_good_set_of_cells_that_should_be_settable_at_runtime if cells_that_can_be_set == :named_references_only
|
484
|
+
cells_that_can_be_set_due_to_named_reference = Hash.new { |h,k| h[k] = Array.new }
|
480
485
|
@named_references_that_can_be_set_at_runtime = []
|
481
486
|
all_named_references = named_references
|
482
487
|
@named_references_to_keep.each do |name|
|
@@ -484,23 +489,34 @@ class ExcelToX
|
|
484
489
|
if ref.first == :sheet_reference
|
485
490
|
sheet = ref[1]
|
486
491
|
cell = ref[2][1].gsub('$','')
|
487
|
-
s =
|
488
|
-
|
492
|
+
s = cells_that_can_be_set[sheet]
|
493
|
+
if s && s.include?(cell)
|
494
|
+
@named_references_that_can_be_set_at_runtime << name
|
495
|
+
cells_that_can_be_set_due_to_named_reference[sheet] << cell
|
496
|
+
cells_that_can_be_set_due_to_named_reference[sheet].uniq!
|
497
|
+
end
|
489
498
|
elsif ref.first.is_a?(Array)
|
490
499
|
ref = ref.first
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
500
|
+
settable = ref.all? do |r|
|
501
|
+
sheet = r[1]
|
502
|
+
cell = r[2][1].gsub('$','')
|
503
|
+
s = cells_that_can_be_set[sheet]
|
504
|
+
s && s.include?(cell)
|
505
|
+
end
|
506
|
+
if settable
|
507
|
+
@named_references_that_can_be_set_at_runtime << name
|
508
|
+
ref.each do |r|
|
509
|
+
sheet = r[1]
|
510
|
+
cell = r[2][1].gsub('$','')
|
511
|
+
cells_that_can_be_set_due_to_named_reference[sheet] << cell
|
512
|
+
cells_that_can_be_set_due_to_named_reference[sheet].uniq!
|
499
513
|
end
|
500
514
|
end
|
501
|
-
@named_references_that_can_be_set_at_runtime << name if settable
|
502
515
|
end
|
503
516
|
end
|
517
|
+
if @cells_that_can_be_set_at_runtime == :named_references_only
|
518
|
+
@cells_that_can_be_set_at_runtime = cells_that_can_be_set_due_to_named_reference
|
519
|
+
end
|
504
520
|
end
|
505
521
|
|
506
522
|
# FIXME: Feels like a kludge
|
@@ -510,7 +526,7 @@ class ExcelToX
|
|
510
526
|
|
511
527
|
i = input('Named references')
|
512
528
|
o = intermediate('Named references to keep')
|
513
|
-
i.
|
529
|
+
i.each_line do |line|
|
514
530
|
sheet, name, ref = *line.split("\t")
|
515
531
|
key = sheet.length != 0 ? [sheet, name] : name
|
516
532
|
o.puts line if named_references_to_keep.include?(key) || named_references_that_can_be_set_at_runtime.include?(key)
|
@@ -519,7 +535,7 @@ class ExcelToX
|
|
519
535
|
|
520
536
|
i.rewind
|
521
537
|
o = intermediate('Named references to set')
|
522
|
-
i.
|
538
|
+
i.each_line do |line|
|
523
539
|
sheet, name, ref = *line.split("\t")
|
524
540
|
key = sheet.length != 0 ? [sheet, name] : name
|
525
541
|
o.puts line if named_references_that_can_be_set_at_runtime.include?(key)
|
@@ -550,25 +566,35 @@ class ExcelToX
|
|
550
566
|
end
|
551
567
|
end
|
552
568
|
|
553
|
-
# FIXME: This should work out how often it needs to operate, rather than having a hardwired 4
|
554
569
|
def replace_formulae_with_their_results
|
555
|
-
|
556
|
-
|
570
|
+
number_of_passes = 0
|
571
|
+
begin
|
572
|
+
number_of_passes += 1
|
573
|
+
@replacements_made_in_the_last_pass = 0
|
574
|
+
replace_indirects_and_offsets
|
557
575
|
replace_formulae_with_calculated_values
|
558
576
|
replace_references_to_values_with_values
|
559
|
-
|
577
|
+
log.info "Pass #{number_of_passes}: Made #{@replacements_made_in_the_last_pass} replacements"
|
578
|
+
if number_of_passes > 20
|
579
|
+
log.warn "Made more than 20 passes, so aborting"
|
580
|
+
break
|
581
|
+
end
|
582
|
+
end while @replacements_made_in_the_last_pass > 0
|
560
583
|
end
|
561
584
|
|
562
|
-
# There is no support for INDIRECT in the ruby or c runtime
|
585
|
+
# There is no support for INDIRECT or OFFSET in the ruby or c runtime
|
563
586
|
# However, in many cases it isn't needed, because we can work
|
564
|
-
# out the value of the indirect at compile time and eliminate it
|
565
|
-
def
|
587
|
+
# out the value of the indirect or OFFSET at compile time and eliminate it
|
588
|
+
def replace_indirects_and_offsets
|
566
589
|
worksheets do |name,xml_filename|
|
567
|
-
log.info "Replacing
|
590
|
+
log.info "Replacing INDIRECT, OFFSET and COLUMN functions in #{name}"
|
568
591
|
|
569
592
|
# First of all we replace any indirects where their values can be calculated at compile time with those
|
570
|
-
# calculated values (e.g., INDIRECT("A"&1) can be turned into A1)
|
571
|
-
|
593
|
+
# calculated values (e.g., INDIRECT("A"&1) can be turned into A1 and OFFSET(A1,1,1,2,2) can be turned into B2:C3)
|
594
|
+
[ReplaceIndirectsWithReferences.new, ReplaceOffsetsWithReferences.new, ReplaceColumnWithColumnNumber.new].each do |r|
|
595
|
+
replace r, [name, 'Formulae'], [name, 'Formulae']
|
596
|
+
@replacements_made_in_the_last_pass += r.replacements_made_in_the_last_pass
|
597
|
+
end
|
572
598
|
|
573
599
|
# The result of the indirect might be a named reference, which we need to simplify
|
574
600
|
r = ReplaceNamedReferences.new
|
@@ -589,7 +615,10 @@ class ExcelToX
|
|
589
615
|
# If a formula's value can be calculated at compile time, it is replaced with its calculated value (e.g., 1+1 gets replaced with 2)
|
590
616
|
def replace_formulae_with_calculated_values
|
591
617
|
worksheets do |name,xml_filename|
|
592
|
-
|
618
|
+
r = ReplaceFormulaeWithCalculatedValues.new
|
619
|
+
r.excel_file = excel_file
|
620
|
+
replace r, [name, 'Formulae'], [name, 'Formulae']
|
621
|
+
@replacements_made_in_the_last_pass += r.replacements_made_in_the_last_pass
|
593
622
|
end
|
594
623
|
end
|
595
624
|
|
@@ -623,6 +652,7 @@ class ExcelToX
|
|
623
652
|
r.default_sheet_name = name
|
624
653
|
replace r, [name, 'Formulae'], [name, 'Formulae']
|
625
654
|
end
|
655
|
+
@replacements_made_in_the_last_pass += r.replacements_made_in_the_last_pass
|
626
656
|
end
|
627
657
|
|
628
658
|
# If 'cells to keep' are specified, then other cells are removed, unless
|
@@ -773,10 +803,11 @@ class ExcelToX
|
|
773
803
|
# or in cells_that_can_be_set_at_runtime, then we assume that
|
774
804
|
# all value cells should be settable if they are referenced by
|
775
805
|
# any other forumla.
|
776
|
-
def
|
806
|
+
def a_good_set_of_cells_that_should_be_settable_at_runtime
|
777
807
|
references = all_formulae
|
778
808
|
counter = CountFormulaReferences.new
|
779
809
|
count = counter.count(references)
|
810
|
+
settable_cells = {}
|
780
811
|
|
781
812
|
count.each do |sheet,keys|
|
782
813
|
keys.each do |ref,count|
|
@@ -784,11 +815,12 @@ class ExcelToX
|
|
784
815
|
ast = references[sheet][ref]
|
785
816
|
next unless ast
|
786
817
|
if [:blank,:number,:null,:string,:shared_string,:constant,:percentage,:error,:boolean_true,:boolean_false].include?(ast.first)
|
787
|
-
|
788
|
-
|
818
|
+
settable_cells[sheet] ||= []
|
819
|
+
settable_cells[sheet] << ref.upcase
|
789
820
|
end
|
790
821
|
end
|
791
|
-
end
|
822
|
+
end
|
823
|
+
return settable_cells
|
792
824
|
end
|
793
825
|
|
794
826
|
# UTILITY FUNCTIONS
|
@@ -820,7 +852,7 @@ class ExcelToX
|
|
820
852
|
worksheets do |name,xml_filename|
|
821
853
|
r = references[name] = {}
|
822
854
|
i = input([name,'Formulae'])
|
823
|
-
i.
|
855
|
+
i.each_line do |line|
|
824
856
|
line =~ /^(.*?)\t(.*)$/
|
825
857
|
ref, ast = $1, $2
|
826
858
|
r[ref] = eval(ast)
|
@@ -841,7 +873,7 @@ class ExcelToX
|
|
841
873
|
def worksheets(&block)
|
842
874
|
unless @worksheet_filenames
|
843
875
|
worksheet_names = input('Worksheet names')
|
844
|
-
@worksheet_filenames = worksheet_names.
|
876
|
+
@worksheet_filenames = worksheet_names.each_line.map do |line|
|
845
877
|
name, filename = *line.split("\t")
|
846
878
|
[name, filename.strip]
|
847
879
|
end
|
data/src/compile/c/a.out
CHANGED
Binary file
|
@@ -74,7 +74,7 @@ class CompileNamedReferenceSetters
|
|
74
74
|
mapper.sheet_names = Hash[sheet_names.readlines.map { |line| line.strip.split("\t")}]
|
75
75
|
mapper.cells_that_can_be_set_at_runtime = cells_that_can_be_set_at_runtime
|
76
76
|
|
77
|
-
named_references.
|
77
|
+
named_references.each_line do |line|
|
78
78
|
name, reference = line.split("\t")
|
79
79
|
ast = eval(reference)
|
80
80
|
output.puts "void set_#{name}(ExcelValue newValue) {"
|
@@ -19,7 +19,7 @@ class CompileToC
|
|
19
19
|
mapper.worksheet = worksheet
|
20
20
|
mapper.sheet_names = Hash[sheet_names_file.readlines.map { |line| line.strip.split("\t")}]
|
21
21
|
c_name = mapper.sheet_names[worksheet] || worksheet
|
22
|
-
input.
|
22
|
+
input.each_line do |line|
|
23
23
|
begin
|
24
24
|
ref, formula = line.split("\t")
|
25
25
|
ast = eval(formula)
|
@@ -12,7 +12,7 @@ class CompileToCHeader
|
|
12
12
|
c_name = Hash[sheet_names_file.readlines.map { |line| line.strip.split("\t")}][worksheet]
|
13
13
|
self.gettable ||= lambda { |ref| true }
|
14
14
|
self.settable ||= lambda { |ref| false }
|
15
|
-
input.
|
15
|
+
input.each_line do |line|
|
16
16
|
begin
|
17
17
|
ref, formula = line.split("\t")
|
18
18
|
static_or_not = (gettable.call(ref) || settable.call(ref)) ? "" : "static "
|
@@ -67,6 +67,9 @@ static ExcelValue mod(ExcelValue a_v, ExcelValue b_v);
|
|
67
67
|
static ExcelValue negative(ExcelValue a_v);
|
68
68
|
static ExcelValue pmt(ExcelValue rate_v, ExcelValue number_of_periods_v, ExcelValue present_value_v);
|
69
69
|
static ExcelValue power(ExcelValue a_v, ExcelValue b_v);
|
70
|
+
static ExcelValue pv_3(ExcelValue a_v, ExcelValue b_v, ExcelValue c_v);
|
71
|
+
static ExcelValue pv_4(ExcelValue a_v, ExcelValue b_v, ExcelValue c_v, ExcelValue d_v);
|
72
|
+
static ExcelValue pv_5(ExcelValue a_v, ExcelValue b_v, ExcelValue c_v, ExcelValue d_v, ExcelValue e_v);
|
70
73
|
static ExcelValue excel_round(ExcelValue number_v, ExcelValue decimal_places_v);
|
71
74
|
static ExcelValue rounddown(ExcelValue number_v, ExcelValue decimal_places_v);
|
72
75
|
static ExcelValue roundup(ExcelValue number_v, ExcelValue decimal_places_v);
|
@@ -77,6 +80,7 @@ static ExcelValue sumifs(ExcelValue sum_range_v, int number_of_arguments, ExcelV
|
|
77
80
|
static ExcelValue sumif(ExcelValue check_range_v, ExcelValue criteria_v, ExcelValue sum_range_v );
|
78
81
|
static ExcelValue sumif_2(ExcelValue check_range_v, ExcelValue criteria_v);
|
79
82
|
static ExcelValue sumproduct(int number_of_arguments, ExcelValue *arguments);
|
83
|
+
static ExcelValue text(ExcelValue number_v, ExcelValue format_v);
|
80
84
|
static ExcelValue vlookup_3(ExcelValue lookup_value_v,ExcelValue lookup_table_v, ExcelValue column_number_v);
|
81
85
|
static ExcelValue vlookup(ExcelValue lookup_value_v,ExcelValue lookup_table_v, ExcelValue column_number_v, ExcelValue match_type_v);
|
82
86
|
|
@@ -122,7 +126,7 @@ static ExcelValue new_excel_number(double number) {
|
|
122
126
|
static ExcelValue new_excel_string(char *string) {
|
123
127
|
cell_counter++;
|
124
128
|
HEAPCHECK
|
125
|
-
ExcelValue new_cell =
|
129
|
+
ExcelValue new_cell = cells[cell_counter];
|
126
130
|
new_cell.type = ExcelString;
|
127
131
|
new_cell.string = string;
|
128
132
|
return new_cell;
|
@@ -1071,6 +1075,57 @@ static ExcelValue pmt(ExcelValue rate_v, ExcelValue number_of_periods_v, ExcelVa
|
|
1071
1075
|
return new_excel_number(-present_value*(rate*(pow((1+rate),number_of_periods)))/((pow((1+rate),number_of_periods))-1));
|
1072
1076
|
}
|
1073
1077
|
|
1078
|
+
static ExcelValue pv_3(ExcelValue rate_v, ExcelValue nper_v, ExcelValue pmt_v) {
|
1079
|
+
return pv_4(rate_v, nper_v, pmt_v, ZERO);
|
1080
|
+
}
|
1081
|
+
|
1082
|
+
static ExcelValue pv_4(ExcelValue rate_v, ExcelValue nper_v, ExcelValue pmt_v, ExcelValue fv_v) {
|
1083
|
+
return pv_5(rate_v, nper_v, pmt_v, fv_v, ZERO);
|
1084
|
+
}
|
1085
|
+
|
1086
|
+
static ExcelValue pv_5(ExcelValue rate_v, ExcelValue nper_v, ExcelValue pmt_v, ExcelValue fv_v, ExcelValue type_v ) {
|
1087
|
+
CHECK_FOR_PASSED_ERROR(rate_v)
|
1088
|
+
CHECK_FOR_PASSED_ERROR(nper_v)
|
1089
|
+
CHECK_FOR_PASSED_ERROR(pmt_v)
|
1090
|
+
CHECK_FOR_PASSED_ERROR(fv_v)
|
1091
|
+
CHECK_FOR_PASSED_ERROR(type_v)
|
1092
|
+
|
1093
|
+
NUMBER(rate_v, rate)
|
1094
|
+
NUMBER(nper_v, nper)
|
1095
|
+
NUMBER(pmt_v, payment)
|
1096
|
+
NUMBER(fv_v, fv)
|
1097
|
+
NUMBER(type_v, start_of_period)
|
1098
|
+
CHECK_FOR_CONVERSION_ERROR
|
1099
|
+
|
1100
|
+
if(rate< 0) {
|
1101
|
+
return VALUE;
|
1102
|
+
}
|
1103
|
+
|
1104
|
+
double present_value = 0;
|
1105
|
+
|
1106
|
+
// Sum up the payments
|
1107
|
+
if(rate == 0) {
|
1108
|
+
present_value = -payment * nper;
|
1109
|
+
} else {
|
1110
|
+
present_value = -payment * ((1-pow(1+rate,-nper))/rate);
|
1111
|
+
}
|
1112
|
+
|
1113
|
+
// Adjust for beginning or end of period
|
1114
|
+
if(start_of_period == 0) {
|
1115
|
+
// Do Nothing
|
1116
|
+
} else if(start_of_period == 1) {
|
1117
|
+
present_value = present_value * (1+rate);
|
1118
|
+
} else {
|
1119
|
+
return VALUE;
|
1120
|
+
}
|
1121
|
+
|
1122
|
+
// Add on the final value
|
1123
|
+
present_value = present_value - (fv/pow(1+rate,nper));
|
1124
|
+
|
1125
|
+
return new_excel_number(present_value);
|
1126
|
+
}
|
1127
|
+
|
1128
|
+
|
1074
1129
|
static ExcelValue power(ExcelValue a_v, ExcelValue b_v) {
|
1075
1130
|
CHECK_FOR_PASSED_ERROR(a_v)
|
1076
1131
|
CHECK_FOR_PASSED_ERROR(b_v)
|
@@ -1517,6 +1572,56 @@ static ExcelValue sumproduct(int number_of_arguments, ExcelValue *arguments) {
|
|
1517
1572
|
return new_excel_number(sum);
|
1518
1573
|
}
|
1519
1574
|
|
1575
|
+
static ExcelValue text(ExcelValue number_v, ExcelValue format_v) {
|
1576
|
+
CHECK_FOR_PASSED_ERROR(number_v)
|
1577
|
+
CHECK_FOR_PASSED_ERROR(format_v)
|
1578
|
+
|
1579
|
+
char *s;
|
1580
|
+
char *p;
|
1581
|
+
double n;
|
1582
|
+
ExcelValue result;
|
1583
|
+
|
1584
|
+
if(number_v.type == ExcelEmpty) {
|
1585
|
+
number_v = ZERO;
|
1586
|
+
}
|
1587
|
+
|
1588
|
+
if(format_v.type == ExcelEmpty) {
|
1589
|
+
return new_excel_string("");
|
1590
|
+
}
|
1591
|
+
|
1592
|
+
if(number_v.type == ExcelString) {
|
1593
|
+
s = number_v.string;
|
1594
|
+
if (s == NULL || *s == '\0' || isspace(*s)) {
|
1595
|
+
number_v = ZERO;
|
1596
|
+
}
|
1597
|
+
n = strtod (s, &p);
|
1598
|
+
if(*p == '\0') {
|
1599
|
+
number_v = new_excel_number(n);
|
1600
|
+
}
|
1601
|
+
}
|
1602
|
+
|
1603
|
+
if(number_v.type != ExcelNumber) {
|
1604
|
+
return number_v;
|
1605
|
+
}
|
1606
|
+
|
1607
|
+
if(format_v.type != ExcelString) {
|
1608
|
+
return format_v;
|
1609
|
+
}
|
1610
|
+
|
1611
|
+
if(format_v.string == "0%") {
|
1612
|
+
// FIXME: Too little?
|
1613
|
+
s = malloc(100);
|
1614
|
+
free_later(s);
|
1615
|
+
sprintf(s, "%d%%",(int) round(number_v.number*100));
|
1616
|
+
result = new_excel_string(s);
|
1617
|
+
} else {
|
1618
|
+
return format_v;
|
1619
|
+
}
|
1620
|
+
|
1621
|
+
// inspect_excel_value(result);
|
1622
|
+
return result;
|
1623
|
+
}
|
1624
|
+
|
1520
1625
|
static ExcelValue vlookup_3(ExcelValue lookup_value_v,ExcelValue lookup_table_v, ExcelValue column_number_v) {
|
1521
1626
|
return vlookup(lookup_value_v,lookup_table_v,column_number_v,TRUE);
|
1522
1627
|
}
|
@@ -2165,7 +2270,18 @@ int test_functions() {
|
|
2165
2270
|
ExcelValue sum_array_0_v = new_excel_range(sum_array_0,3,1);
|
2166
2271
|
ExcelValue sum_array_1[] = {sum_array_0_v};
|
2167
2272
|
assert(sum(1,sum_array_1).number == 1253.8718091935484);
|
2273
|
+
|
2274
|
+
// Test PV
|
2275
|
+
assert((int) pv_3(new_excel_number(0.03), new_excel_number(12), new_excel_number(100)).number == -995);
|
2276
|
+
assert((int) pv_4(new_excel_number(0.03), new_excel_number(12), new_excel_number(-100), new_excel_number(100)).number == 925);
|
2277
|
+
assert((int) pv_5(new_excel_number(0.03), new_excel_number(12), new_excel_number(-100), new_excel_number(-100), new_excel_number(1)).number == 1095);
|
2168
2278
|
|
2279
|
+
// Test TEXT
|
2280
|
+
assert(strcmp(text(new_excel_number(1.0), new_excel_string("0%")).string, "100%") == 0);
|
2281
|
+
assert(strcmp(text(new_excel_string("1"), new_excel_string("0%")).string, "100%") == 0);
|
2282
|
+
assert(strcmp(text(BLANK, new_excel_string("0%")).string, "0%") == 0);
|
2283
|
+
assert(strcmp(text(new_excel_number(1.0), BLANK).string, "") == 0);
|
2284
|
+
assert(strcmp(text(new_excel_string("ASGASD"), new_excel_string("0%")).string, "ASGASD") == 0);
|
2169
2285
|
// Release memory
|
2170
2286
|
free_all_allocated_memory();
|
2171
2287
|
|