excel_to_code 0.3.17 → 0.3.18.beta.1

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.
Files changed (61) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +67 -34
  3. data/bin/excel_to_c +8 -78
  4. data/bin/excel_to_go +41 -0
  5. data/bin/excel_to_ruby +2 -69
  6. data/src/commands.rb +2 -0
  7. data/src/commands/common_command_line_options.rb +81 -0
  8. data/src/commands/excel_to_c.rb +3 -0
  9. data/src/commands/excel_to_go.rb +91 -0
  10. data/src/commands/excel_to_x.rb +77 -11
  11. data/src/compile.rb +1 -0
  12. data/src/compile/c/a.out +0 -0
  13. data/src/compile/c/a.out.dSYM/Contents/Resources/DWARF/a.out +0 -0
  14. data/src/compile/c/compile_to_c.rb +2 -0
  15. data/src/compile/c/excel_to_c_runtime.c +691 -145
  16. data/src/compile/c/excel_to_c_runtime_test.c +226 -20
  17. data/src/compile/c/map_formulae_to_c.rb +62 -23
  18. data/src/compile/c/run_c_unit_tests +3 -0
  19. data/src/compile/cd.rb +6 -0
  20. data/src/compile/go.rb +3 -0
  21. data/src/compile/go/compile_to_go.rb +85 -0
  22. data/src/compile/go/compile_to_go_test.rb +73 -0
  23. data/src/compile/go/excel.go +171 -0
  24. data/src/compile/go/excel_test.go +54 -0
  25. data/src/compile/go/map_values_to_go.rb +67 -0
  26. data/src/compile/ruby/map_formulae_to_ruby.rb +30 -12
  27. data/src/excel/excel_functions.rb +26 -1
  28. data/src/excel/excel_functions/ceiling.rb +23 -0
  29. data/src/excel/excel_functions/countif.rb +15 -0
  30. data/src/excel/excel_functions/countifs.rb +10 -0
  31. data/src/excel/excel_functions/floor.rb +14 -0
  32. data/src/excel/excel_functions/hyperlink.rb +9 -0
  33. data/src/excel/excel_functions/na.rb +7 -0
  34. data/src/excel/excel_functions/not.rb +13 -0
  35. data/src/excel/excel_functions/or.rb +30 -0
  36. data/src/excel/excel_functions/product.rb +8 -0
  37. data/src/excel/excel_functions/rate.rb +16 -0
  38. data/src/excel/excel_functions/replace.rb +13 -0
  39. data/src/excel/excel_functions/scurve.rb +73 -0
  40. data/src/excel/excel_functions/sqrt.rb +11 -0
  41. data/src/excel/excel_functions/string_argument.rb +37 -0
  42. data/src/excel/excel_functions/sumifs.rb +19 -8
  43. data/src/excel/excel_functions/text.rb +3 -3
  44. data/src/excel/formula_peg.rb +1 -1
  45. data/src/excel/formula_peg.txt +2 -3
  46. data/src/excel/table.rb +15 -15
  47. data/src/excel_to_code.rb +1 -4
  48. data/src/extract/extract_data_from_worksheet.rb +8 -1
  49. data/src/rewrite/ast_expand_array_formulae.rb +4 -0
  50. data/src/rewrite/caching_formula_parser.rb +16 -11
  51. data/src/simplify.rb +1 -0
  52. data/src/simplify/inline_formulae.rb +16 -0
  53. data/src/simplify/replace_arithmetic_on_ranges.rb +14 -1
  54. data/src/simplify/replace_arrays_with_single_cells.rb +42 -15
  55. data/src/simplify/replace_cell_addresses_with_references.rb +70 -0
  56. data/src/simplify/replace_column_with_column_number.rb +8 -1
  57. data/src/simplify/replace_table_references.rb +40 -19
  58. data/src/simplify/simplify_arithmetic.rb +15 -10
  59. data/src/version.rb +4 -0
  60. metadata +115 -43
  61. data/TODO +0 -25
@@ -1,10 +1,10 @@
1
1
  require_relative 'map_values_to_ruby'
2
2
 
3
3
  class MapFormulaeToRuby < MapValuesToRuby
4
-
4
+
5
5
  attr_accessor :sheet_names
6
6
  attr_accessor :worksheet
7
-
7
+
8
8
  FUNCTIONS = {
9
9
  :'*' => 'multiply',
10
10
  :'+' => 'add',
@@ -21,6 +21,8 @@ class MapFormulaeToRuby < MapValuesToRuby
21
21
  :'AND' => 'excel_and',
22
22
  :'AVERAGE' => 'average',
23
23
  :'AVERAGEIFS' => 'averageifs',
24
+ :'CEILING' => 'ceiling',
25
+ :'_xlfn.CEILING.MATH' => 'ceiling',
24
26
  :'CELL' => 'cell',
25
27
  :'CHAR' => 'char',
26
28
  :'CHOOSE' => 'choose',
@@ -28,11 +30,15 @@ class MapFormulaeToRuby < MapValuesToRuby
28
30
  :'COSH' => 'cosh',
29
31
  :'COUNT' => 'count',
30
32
  :'COUNTA' => 'counta',
33
+ :'COUNTIF' => 'countif',
34
+ :'COUNTIFS' => 'countifs',
31
35
  :'ENSURE_IS_NUMBER' => 'ensure_is_number',
32
36
  :'EXP' => 'exp',
33
37
  :'FIND' => 'find',
38
+ :'FLOOR' => 'floor',
34
39
  :'FORECAST' => 'forecast',
35
40
  :'HLOOKUP' => 'hlookup',
41
+ :'HYPERLINK' => 'hyperlink',
36
42
  :'IF' => 'excel_if',
37
43
  :'IFERROR' => 'iferror',
38
44
  :'INDEX' => 'index',
@@ -54,17 +60,24 @@ class MapFormulaeToRuby < MapValuesToRuby
54
60
  :'MIN' => 'min',
55
61
  :'MMULT' => 'mmult',
56
62
  :'MOD' => 'mod',
63
+ :'NA' => 'na',
64
+ :'NOT' => 'excel_not',
57
65
  :'NPV' => 'npv',
58
66
  :'NUMBER_OR_ZERO' => 'number_or_zero',
67
+ :'OR' => 'excel_or',
59
68
  :'PI' => 'pi',
60
69
  :'PMT' => 'pmt',
61
70
  :'POWER' => 'power',
71
+ :'PRODUCT' => 'product',
62
72
  :'PV' => 'pv',
63
73
  :'RANK' => 'rank',
74
+ :'RATE' => 'rate',
75
+ :'REPLACE' => 'replace',
64
76
  :'RIGHT' => 'right',
65
77
  :'ROUND' => 'round',
66
78
  :'ROUNDDOWN' => 'rounddown',
67
79
  :'ROUNDUP' => 'roundup',
80
+ :'SQRT' => 'sqrt',
68
81
  :'SUBSTITUTE' => 'substitute',
69
82
  :'SUBTOTAL' => 'subtotal',
70
83
  :'SUM' => 'sum',
@@ -75,18 +88,23 @@ class MapFormulaeToRuby < MapValuesToRuby
75
88
  :'TRIM' => 'trim',
76
89
  :'VALUE' => 'value',
77
90
  :'VLOOKUP' => 'vlookup',
78
- :'^' => 'power'
91
+ :'^' => 'power',
92
+ :'_xlfn.FORECAST.LINEAR' => 'forecast',
93
+ :'curve' => 'curve',
94
+ :'halfscurve' => 'halfscurve',
95
+ :'lcurve' => 'lcurve',
96
+ :'scurve' => 'scurve'
79
97
  }
80
-
98
+
81
99
  def prefix(symbol,ast)
82
100
  return map(ast) if symbol == "+"
83
101
  return "negative(#{map(ast)})"
84
102
  end
85
-
103
+
86
104
  def brackets(*contents)
87
105
  "(#{contents.map { |a| map(a) }.join(',')})"
88
106
  end
89
-
107
+
90
108
  def arithmetic(left,operator,right)
91
109
  "#{FUNCTIONS[operator.last]}(#{map(left)},#{map(right)})"
92
110
  end
@@ -103,7 +121,7 @@ class MapFormulaeToRuby < MapValuesToRuby
103
121
  def comparison(left,operator,right)
104
122
  "#{FUNCTIONS[operator.last]}(#{map(left)},#{map(right)})"
105
123
  end
106
-
124
+
107
125
  def function(function_name,*arguments)
108
126
  if FUNCTIONS.has_key?(function_name)
109
127
  "#{FUNCTIONS[function_name]}(#{arguments.map { |a| map(a) }.join(",")})"
@@ -111,21 +129,21 @@ class MapFormulaeToRuby < MapValuesToRuby
111
129
  raise NotSupportedException.new("Function #{function_name} not supported")
112
130
  end
113
131
  end
114
-
132
+
115
133
  def cell(reference)
116
134
  reference.to_s.downcase.gsub('$','')
117
135
  end
118
-
136
+
119
137
  def sheet_reference(sheet,reference)
120
138
  "#{sheet_names[sheet]}_#{map(reference)}"
121
139
  end
122
-
140
+
123
141
  def array(*rows)
124
142
  "[#{rows.map {|r| map(r)}.join(",")}]"
125
143
  end
126
-
144
+
127
145
  def row(*cells)
128
146
  "[#{cells.map {|r| map(r)}.join(",")}]"
129
147
  end
130
-
148
+
131
149
  end
@@ -3,6 +3,7 @@ end
3
3
 
4
4
  # Support functions
5
5
  require_relative 'excel_functions/number_argument'
6
+ require_relative 'excel_functions/string_argument'
6
7
  require_relative 'excel_functions/reset'
7
8
 
8
9
  # Constants
@@ -31,6 +32,7 @@ require_relative 'excel_functions/negative'
31
32
  require_relative 'excel_functions/abs'
32
33
  require_relative 'excel_functions/and'
33
34
  require_relative 'excel_functions/mod'
35
+ require_relative 'excel_functions/not'
34
36
 
35
37
  # Array arithmetic
36
38
  require_relative 'excel_functions/sum'
@@ -71,7 +73,6 @@ require_relative 'excel_functions/int'
71
73
 
72
74
  # Other functions
73
75
 
74
-
75
76
  require_relative 'excel_functions/cell'
76
77
 
77
78
  require_relative 'excel_functions/trim'
@@ -123,3 +124,27 @@ require_relative 'excel_functions/char'
123
124
  require_relative 'excel_functions/address'
124
125
 
125
126
  require_relative 'excel_functions/number_or_zero'
127
+
128
+ require_relative 'excel_functions/countif'
129
+
130
+ require_relative 'excel_functions/hyperlink'
131
+
132
+ require_relative 'excel_functions/na'
133
+
134
+ require_relative 'excel_functions/or'
135
+
136
+ require_relative 'excel_functions/scurve'
137
+
138
+ require_relative 'excel_functions/floor'
139
+
140
+ require_relative 'excel_functions/sqrt'
141
+
142
+ require_relative 'excel_functions/product'
143
+
144
+ require_relative 'excel_functions/countifs'
145
+
146
+ require_relative 'excel_functions/replace'
147
+
148
+ require_relative 'excel_functions/rate'
149
+
150
+ require_relative 'excel_functions/ceiling'
@@ -0,0 +1,23 @@
1
+ module ExcelFunctions
2
+
3
+ def ceiling(number, multiple, mode = 0)
4
+ return number if number.is_a?(Symbol)
5
+ return multiple if multiple.is_a?(Symbol)
6
+ return mode if mode.is_a?(Symbol)
7
+ number = number_argument(number)
8
+ multiple = number_argument(multiple)
9
+ mode = number_argument(mode)
10
+ return :value unless number.is_a?(Numeric)
11
+ return :value unless multiple.is_a?(Numeric)
12
+ return :value unless mode.is_a?(Numeric)
13
+ return 0 if multiple == 0
14
+ if mode == 0 || number > 0
15
+ whole, remainder = number.divmod(multiple)
16
+ num_steps = remainder > 0 ? whole + 1 : whole
17
+ num_steps * multiple
18
+ else # Need to round negative away from zero
19
+ -ceiling(-number, multiple, 0)
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,15 @@
1
+ module ExcelFunctions
2
+
3
+ def countif(count_range, criteria)
4
+ return 0 if criteria.kind_of?(Array) # Who knows why, Excel
5
+ count_range = [count_range] unless count_range.kind_of?(Array)
6
+ rows = count_range.size
7
+ if count_range.first && count_range.first.kind_of?(Array)
8
+ sum_range = Array.new(rows, Array.new(count_range.first.size,1))
9
+ else
10
+ sum_range = Array.new(rows, 1)
11
+ end
12
+ return sumif(count_range, criteria, sum_range)
13
+ end
14
+
15
+ end
@@ -0,0 +1,10 @@
1
+ module ExcelFunctions
2
+
3
+ def countifs(*criteria)
4
+ return 0 if criteria.empty?
5
+ c = criteria[0].is_a?(Array) ? criteria[0] : [criteria[0]]
6
+ count = c.map { 1 }
7
+ return sumifs(count, *criteria)
8
+ end
9
+
10
+ end
@@ -0,0 +1,14 @@
1
+ module ExcelFunctions
2
+
3
+ def floor(number, multiple)
4
+ return number if number.is_a?(Symbol)
5
+ return multiple if multiple.is_a?(Symbol)
6
+ number ||= 0
7
+ return :value unless number.is_a?(Numeric)
8
+ return :value unless multiple.is_a?(Numeric)
9
+ return :div0 if multiple == 0
10
+ return :num if multiple < 0
11
+ number - (number % multiple)
12
+ end
13
+
14
+ end
@@ -0,0 +1,9 @@
1
+ module ExcelFunctions
2
+
3
+ def hyperlink(url, text = url)
4
+ u = string_argument(url)
5
+ t = string_argument(text)
6
+ "<a href=#{u.inspect}>#{t}</a>"
7
+ end
8
+
9
+ end
@@ -0,0 +1,7 @@
1
+ module ExcelFunctions
2
+
3
+ def na()
4
+ return :na
5
+ end
6
+
7
+ end
@@ -0,0 +1,13 @@
1
+ module ExcelFunctions
2
+
3
+ def excel_not(a)
4
+ return a if a.is_a?(Symbol)
5
+ return :value if a.is_a?(String)
6
+ return :value if a.is_a?(Array)
7
+ a = false if a == nil
8
+ a = false if a == 0
9
+ a = true if a.is_a?(Numeric)
10
+ !a
11
+ end
12
+
13
+ end
@@ -0,0 +1,30 @@
1
+ module ExcelFunctions
2
+
3
+ def excel_or(*args)
4
+ # Flatten arrays
5
+ args = args.flatten
6
+
7
+ # If an argument is an error, return that
8
+ error = args.find {|a| a.is_a?(Symbol)}
9
+ return error if error
10
+
11
+ # Replace 1 and 0 with true and false
12
+ args.map! do |a|
13
+ case a
14
+ when 1; true
15
+ when 0; false
16
+ else; a
17
+ end
18
+ end
19
+
20
+ # Remove anything not boolean
21
+ args.delete_if { |a| !(a.is_a?(TrueClass) || a.is_a?(FalseClass)) }
22
+
23
+ # Return an error if nothing less
24
+ return :value if args.empty?
25
+
26
+ # Now calculate and return
27
+ args.any? {|a| a == true }
28
+ end
29
+
30
+ end
@@ -0,0 +1,8 @@
1
+ module ExcelFunctions
2
+
3
+ def product(*args)
4
+ args = args.flatten
5
+ args.find {|a| a.is_a?(Symbol)} || args.delete_if { |a| !a.is_a?(Numeric) }.inject(0) { |m,i| m + i }
6
+ end
7
+
8
+ end
@@ -0,0 +1,16 @@
1
+ module ExcelFunctions
2
+
3
+ def rate(periods, payment, presentValue, finalValue)
4
+ raise NotSupportedException.new("rate() function has not been implemented fully. Edit src/excel/excel_functions/rate.rb") unless (payment || 0) == 0
5
+ return periods if periods.is_a?(Symbol)
6
+ return presentValue if presentValue.is_a?(Symbol)
7
+ return finalValue if finalValue.is_a?(Symbol)
8
+
9
+ return :num unless periods.is_a?(Numeric)
10
+ return :num unless presentValue.is_a?(Numeric)
11
+ return :num unless finalValue.is_a?(Numeric)
12
+
13
+ ((finalValue.to_f/(-presentValue.to_f)) ** (1.0/periods))-1.0
14
+ end
15
+
16
+ end
@@ -0,0 +1,13 @@
1
+ module ExcelFunctions
2
+
3
+ def replace(string, start, length, replacement)
4
+ return string if string.is_a?(Symbol)
5
+ return start if start.is_a?(Symbol)
6
+ return length if length.is_a?(Symbol)
7
+ return replacement if replacement.is_a?(Symbol)
8
+ string = string.to_s
9
+ return string + replacement if start >= string.length
10
+ string_join(left(string,start-1),replacement,right(string,string.length - (start - 1 + length)))
11
+ end
12
+
13
+ end
@@ -0,0 +1,73 @@
1
+ module ExcelFunctions
2
+
3
+ def scurve(currentYear, startValue, endValue, duration, startYear = 2018.0)
4
+ currentYear = number_argument(currentYear)
5
+ startValue = number_argument(startValue)
6
+ endValue = number_argument(endValue)
7
+ duration = number_argument(duration)
8
+ startYear = number_argument(startYear)
9
+
10
+ return currentYear if currentYear.is_a?(Symbol)
11
+ return startValue if startValue.is_a?(Symbol)
12
+ return endValue if endValue.is_a?(Symbol)
13
+ return duration if duration.is_a?(Symbol)
14
+ return startYear if startYear.is_a?(Symbol)
15
+
16
+ return startValue if currentYear < startYear
17
+
18
+ x = (currentYear - startYear) / duration.to_f
19
+ x0 = 0.0
20
+ a = endValue - startValue
21
+ sc = 0.999
22
+ eps = 1.0 - sc
23
+ mu = 0.5
24
+ beta = (mu - 1.0) / Math.log(1.0 / sc - 1)
25
+ scurve = a * (((Math.exp(-(x - mu) / beta) + 1) ** -1) - ((Math.exp(-(x0 - mu) / beta) + 1) ** -1)) + startValue
26
+ end
27
+
28
+ def halfscurve(currentYear, startValue, endValue , duration, startYear = 2018)
29
+ currentYear = number_argument(currentYear)
30
+ startValue = number_argument(startValue)
31
+ endValue = number_argument(endValue)
32
+ duration = number_argument(duration)
33
+ startYear = number_argument(startYear)
34
+
35
+ return currentYear if currentYear.is_a?(Symbol)
36
+ return startValue if startValue.is_a?(Symbol)
37
+ return endValue if endValue.is_a?(Symbol)
38
+ return duration if duration.is_a?(Symbol)
39
+ return startYear if startYear.is_a?(Symbol)
40
+ return startValue if currentYear < startYear
41
+
42
+ scurve(currentYear + duration, startValue, endValue, duration * 2, startYear) - ((endValue - startValue) / 2.0)
43
+ end
44
+
45
+ def lcurve(currentYear, startValue, endValue , duration, startYear = 2018)
46
+ currentYear = number_argument(currentYear)
47
+ startValue = number_argument(startValue)
48
+ endValue = number_argument(endValue)
49
+ duration = number_argument(duration)
50
+ startYear = number_argument(startYear)
51
+
52
+ return currentYear if currentYear.is_a?(Symbol)
53
+ return startValue if startValue.is_a?(Symbol)
54
+ return endValue if endValue.is_a?(Symbol)
55
+ return duration if duration.is_a?(Symbol)
56
+ return startYear if startYear.is_a?(Symbol)
57
+
58
+ return startValue if currentYear < startYear
59
+ return endValue if currentYear > (startYear + duration)
60
+ return startValue if currentYear < startYear
61
+ startValue + (endValue - startValue) / duration * (currentYear - startYear)
62
+ end
63
+
64
+ def curve(curveType, currentYear, startValue, endValue, duration, startYear = 2018)
65
+ if curveType == "s"
66
+ return scurve(currentYear, startValue, endValue, duration, startYear)
67
+ elsif curveType == "hs"
68
+ return halfscurve(currentYear, startValue, endValue, duration, startYear)
69
+ else
70
+ return lcurve(currentYear, startValue, endValue, duration, startYear)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,11 @@
1
+ module ExcelFunctions
2
+
3
+ def sqrt(a)
4
+ return a if a.is_a?(Symbol)
5
+ a ||= 0
6
+ return :value unless a.is_a?(Numeric)
7
+ return :num if a < 0
8
+ Math.sqrt(a)
9
+ end
10
+
11
+ end
@@ -0,0 +1,37 @@
1
+ module ExcelFunctions
2
+
3
+ # This is a support function for mapping arguments that are not strings to their
4
+ # Excel string equivalent. e.g., true = TRUE, :div0 = "DIV/0!"
5
+ def string_argument(a)
6
+ case a
7
+ when Symbol
8
+ return {
9
+ :name => "#NAME?",
10
+ :value => "#VALUE!",
11
+ :div0 => "#DIV/0!",
12
+ :ref => "#REF!",
13
+ :na => "#N/A",
14
+ :num => "#NUM!",
15
+ }[a] || :value
16
+ when String
17
+ return a
18
+ when nil
19
+ return ""
20
+ when true
21
+ return "TRUE"
22
+ when false
23
+ return "FALSE"
24
+ when Numeric
25
+ if a.round == a
26
+ return a.to_i.to_s
27
+ else
28
+ return a.to_s
29
+ end
30
+ when Array
31
+ return string_argument(a[0][0])
32
+ else
33
+ return :value
34
+ end
35
+ end
36
+
37
+ end