excel_to_code 0.1.8 → 0.1.10
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.
- 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
|
|