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
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ clang excel_to_c_runtime_test.c
3
+ ./a.out
data/src/compile/cd.rb ADDED
@@ -0,0 +1,6 @@
1
+ require_relative 'c/compile_to_c'
2
+ require_relative 'c/compile_to_c_header'
3
+ require_relative 'c/map_sheet_names_to_c_names'
4
+ require_relative "c/compile_to_c_unit_test"
5
+ require_relative "c/map_values_to_c_structs"
6
+ require_relative "c/compile_named_reference_setters"
data/src/compile/go.rb ADDED
@@ -0,0 +1,3 @@
1
+ require_relative 'go/compile_to_go'
2
+ require_relative 'go/compile_to_go_test'
3
+ require_relative 'go/map_values_to_go'
@@ -0,0 +1,85 @@
1
+
2
+ class CompileToGo
3
+
4
+ attr_accessor :settable
5
+ attr_accessor :gettable
6
+ attr_accessor :sheet_names
7
+
8
+ def struct_type
9
+ "spreadsheet"
10
+ end
11
+
12
+ def self.rewrite(*args)
13
+ self.new.rewrite(*args)
14
+ end
15
+
16
+ def rewrite(formulae, sheet_names, output)
17
+ self.settable ||= lambda { |ref| false }
18
+ self.gettable ||= lambda { |ref| true }
19
+ self.sheet_names = sheet_names
20
+
21
+ m = MapValuesToGo.new
22
+
23
+ # The struct
24
+ output.puts "type #{struct_type} struct {"
25
+ formulae.each do |ref, _|
26
+ output.puts " #{variable_name(ref)} cachedValue"
27
+ end
28
+ output.puts "}"
29
+
30
+ # The initializer
31
+ output.puts <<~END
32
+
33
+ func New() #{struct_type} {
34
+ return #{struct_type}{}
35
+ }
36
+
37
+ END
38
+
39
+ formulae.each do |ref, ast|
40
+ v = variable_name(ref)
41
+ output.puts <<~END
42
+ func (s *#{struct_type}) #{getter_method_name(ref)}() (interface{}, error) {
43
+ if !s.#{v}.isCached() {
44
+ s.#{v}.set(#{m.map(ast)})
45
+ }
46
+ return s.#{v}.get()
47
+ }
48
+
49
+ END
50
+ if settable.call(ref)
51
+ output.puts <<~END
52
+
53
+ func (s *#{struct_type}) #{setter_method_name(ref)}(v interface{}) {
54
+ s.#{v}.set(v)
55
+ }
56
+
57
+ END
58
+ end
59
+ end
60
+ end
61
+
62
+ def getter_method_name(ref)
63
+ v = variable_name(ref)
64
+ if gettable.call(ref)
65
+ v[0] = v[0].upcase!
66
+ else
67
+ v[0] = v[0].downcase!
68
+ end
69
+ v
70
+ end
71
+
72
+ def setter_method_name(ref)
73
+ v = variable_name(ref)
74
+ v[0].upcase!
75
+ "Set#{v}"
76
+ end
77
+
78
+ def variable_name(ref)
79
+ worksheet = ref.first
80
+ cell = ref.last
81
+ worksheet_name = sheet_names[worksheet.to_s] || worksheet.to_s
82
+ return worksheet_name.length > 0 ? "#{worksheet_name.downcase}#{cell.upcase}" : cell.downcase
83
+ end
84
+
85
+ end
@@ -0,0 +1,73 @@
1
+
2
+ class CompileToGoTest
3
+
4
+ attr_accessor :settable
5
+ attr_accessor :gettable
6
+ attr_accessor :sheet_names
7
+
8
+ def struct_type
9
+ "spreadsheet"
10
+ end
11
+
12
+ def self.rewrite(*args)
13
+ self.new.rewrite(*args)
14
+ end
15
+
16
+ def rewrite(formulae, sheet_names, output)
17
+ self.settable ||= lambda { |ref| false }
18
+ self.gettable ||= lambda { |ref| true }
19
+ self.sheet_names = sheet_names
20
+
21
+ m = MapValuesToGo.new
22
+
23
+ formulae.each do |ref, ast|
24
+ next unless gettable.call(ref)
25
+ n = getter_method_name(ref)
26
+
27
+ if ast.first == :error
28
+ output.puts <<~END
29
+ func Test#{n}(t *testing.T) {
30
+ s := New()
31
+ e := #{m.map(ast)}
32
+ a, err := s.#{n}()
33
+ if err != e {
34
+ t.Errorf("#{n} = (%v, %v), want (nil, %v)", a, err, e)
35
+ }
36
+ }
37
+
38
+ END
39
+
40
+ else
41
+ output.puts <<~END
42
+ func Test#{n}(t *testing.T) {
43
+ s := New()
44
+ e := #{m.map(ast)}
45
+ a, err := s.#{n}()
46
+ if a != e || err != nil {
47
+ t.Errorf("#{n} = (%v, %v), want (%v, nil)", a, err, e)
48
+ }
49
+ }
50
+
51
+ END
52
+ end
53
+ end
54
+ end
55
+
56
+ def getter_method_name(ref)
57
+ v = variable_name(ref)
58
+ if gettable.call(ref)
59
+ v[0] = v[0].upcase!
60
+ else
61
+ v[0] = v[0].downcase!
62
+ end
63
+ v
64
+ end
65
+
66
+ def variable_name(ref)
67
+ worksheet = ref.first
68
+ cell = ref.last
69
+ worksheet_name = sheet_names[worksheet.to_s] || worksheet.to_s
70
+ return worksheet_name.length > 0 ? "#{worksheet_name.downcase}#{cell.upcase}" : cell.downcase
71
+ end
72
+
73
+ end
@@ -0,0 +1,171 @@
1
+ // excel replicates values and functions from Microsoft Excel
2
+ package excel
3
+
4
+ import (
5
+ "fmt"
6
+ "strconv"
7
+ )
8
+
9
+ // cachedValue wraps the different possible Excel Values
10
+ //
11
+ // Later, we may have one for each Excel type
12
+ // which is why we use wrappers.
13
+ type cachedValue struct {
14
+ // v is the cached value, in this case of any type
15
+ v interface{}
16
+ // c is whether it is cached
17
+ c bool
18
+ }
19
+
20
+ func (c *cachedValue) isCached() bool {
21
+ return c.c
22
+ }
23
+
24
+ func (c *cachedValue) set(v interface{}) {
25
+ c.v = v
26
+ c.c = true
27
+ }
28
+
29
+ // get returns the cached value. If the cached
30
+ // value is an Excel Error that is returned as
31
+ // the second argument. If the item isn't cached
32
+ // then will return a NotCachedError.
33
+ func (c *cachedValue) get() (interface{}, error) {
34
+ if !c.c {
35
+ return nil, notCachedError{}
36
+ }
37
+ if err, ok := c.v.(error); ok {
38
+ return nil, err
39
+ }
40
+ return c.v, nil
41
+ }
42
+
43
+ type notCachedError struct{}
44
+
45
+ func (e notCachedError) Error() string {
46
+ return "Get() called but nothing cached"
47
+ }
48
+
49
+ // Blank is an empty Excel cell.
50
+ type Blank struct{}
51
+
52
+ // The Excel #VALUE! error
53
+ type ValueError struct {
54
+ value interface{}
55
+ cast string
56
+ }
57
+
58
+ // The Excel #NAME? error
59
+ type NameError struct{}
60
+
61
+ // The Excel #DIV/0! error
62
+ type Div0Error struct{}
63
+
64
+ // The Excel #REF! error
65
+ type RefError struct{}
66
+
67
+ // The Excel #N/A error
68
+ type NAError struct{}
69
+
70
+ // The Excel #NUM! error
71
+ type NumError struct{}
72
+
73
+ func (e ValueError) Error() string {
74
+ return fmt.Sprintf("#VALUE!: could not convert %v to %v", e.value, e.cast)
75
+ }
76
+
77
+ func (e NameError) Error() string {
78
+ return "#NAME?"
79
+ }
80
+
81
+ func (e Div0Error) Error() string {
82
+ return "#DIV/0!"
83
+ }
84
+
85
+ func (e RefError) Error() string {
86
+ return "#REF!"
87
+ }
88
+
89
+ func (e NAError) Error() string {
90
+ return "#N/A"
91
+ }
92
+
93
+ func (e NumError) Error() string {
94
+ return "#DIV/0!"
95
+ }
96
+
97
+ func excel_if(c interface{}, t, f func() interface{}) interface{} {
98
+ b, err := boolean(c)
99
+ if err != nil {
100
+ return err
101
+ }
102
+ if b {
103
+ if t == nil {
104
+ return true
105
+ } else {
106
+ return t()
107
+ }
108
+ } else {
109
+ if f == nil {
110
+ return false
111
+ } else {
112
+ return f()
113
+ }
114
+ }
115
+ }
116
+
117
+ func add(a, b interface{}) interface{} {
118
+ n1, err := number(a)
119
+ if err != nil {
120
+ return err
121
+ }
122
+ n2, err := number(b)
123
+ if err != nil {
124
+ return err
125
+ }
126
+ return n1 + n2
127
+ }
128
+
129
+ func number(a interface{}) (float64, error) {
130
+ switch a := a.(type) {
131
+ case Blank:
132
+ return 0, nil
133
+ case float64:
134
+ return a, nil
135
+ case bool:
136
+ if a {
137
+ return 1, nil
138
+ } else {
139
+ return 0, nil
140
+ }
141
+ case error:
142
+ return 0, a
143
+ case string:
144
+ i, err := strconv.ParseFloat(a, 64)
145
+ if err != nil {
146
+ return 0, ValueError{a, "float64"}
147
+ }
148
+ return i, nil
149
+ default:
150
+ return 0, ValueError{a, "float64"}
151
+ }
152
+ }
153
+
154
+ func boolean(a interface{}) (bool, error) {
155
+ switch a := a.(type) {
156
+ case Blank:
157
+ return false, nil
158
+ case float64:
159
+ if a == 0 {
160
+ return false, nil
161
+ } else {
162
+ return true, nil
163
+ }
164
+ case bool:
165
+ return a, nil
166
+ case error:
167
+ return false, a
168
+ default:
169
+ return false, ValueError{a, "bool"}
170
+ }
171
+ }
@@ -0,0 +1,54 @@
1
+ package excel
2
+
3
+ import "testing"
4
+
5
+ type Spreadsheet struct {
6
+ inputA1 interface{}
7
+ inputA2 interface{}
8
+ outputA1 interface{}
9
+ }
10
+
11
+ func (s *Spreadsheet) InputA1() interface{} {
12
+ if s.inputA1 == nil {
13
+ s.inputA1 = Blank{}
14
+ }
15
+ return s.inputA1
16
+ }
17
+
18
+ func (s *Spreadsheet) SetInputA1(value interface{}) {
19
+ s.inputA1 = value
20
+ }
21
+
22
+ func (s *Spreadsheet) InputA2() interface{} {
23
+ if s.inputA2 == nil {
24
+ s.inputA2 = Blank{}
25
+ }
26
+ return s.inputA2
27
+ }
28
+
29
+ func (s *Spreadsheet) SetInputA2(value interface{}) {
30
+ s.inputA2 = value
31
+ }
32
+
33
+ func (s *Spreadsheet) OutputA1() interface{} {
34
+ if s.outputA1 == nil {
35
+ inputA1 := s.InputA1()
36
+ inputA2 := s.InputA2()
37
+ sum := add(inputA1, inputA2)
38
+ s.outputA1 = excel_if(sum, func() interface{} {
39
+ return Blank{}
40
+ }, s.InputA1)
41
+ }
42
+ return s.outputA1
43
+ }
44
+
45
+ func TestHelloWorld(t *testing.T) {
46
+ s := Spreadsheet{}
47
+ s.SetInputA1(1.0)
48
+ s.SetInputA2(1.0)
49
+ //inputA1 := s.InputA1()
50
+ outputA1 := s.OutputA1()
51
+ if outputA1 != 1 {
52
+ t.Errorf("%v = %v, want %v", "s.OutputA1()", outputA1, 1)
53
+ }
54
+ }
@@ -0,0 +1,67 @@
1
+ require_relative '../../util/not_supported_exception'
2
+
3
+ class MapValuesToGo
4
+
5
+ def map(ast)
6
+ if ast.is_a?(Array)
7
+ operator = ast[0]
8
+ if respond_to?(operator)
9
+ send(operator,*ast[1..-1])
10
+ else
11
+ raise NotSupportedException.new("#{operator} in #{ast.inspect} not supported")
12
+ end
13
+ else
14
+ raise NotSupportedException.new("#{ast} not supported")
15
+ end
16
+ end
17
+
18
+ def blank
19
+ "Blank{}"
20
+ end
21
+
22
+ def inlined_blank
23
+ "Blank{}"
24
+ end
25
+
26
+ def constant(name)
27
+ name
28
+ end
29
+
30
+ alias :null :blank
31
+
32
+ def number(text)
33
+ text.to_s
34
+ end
35
+
36
+ def percentage(text)
37
+ (text.to_f / 100.0).to_s
38
+ end
39
+
40
+ def string(text)
41
+ text.inspect
42
+ end
43
+
44
+ ERRORS = {
45
+ :"#NAME?" => "NameError{}",
46
+ :"#VALUE!" => "ValueError{}",
47
+ :"#DIV/0!" => "Div0Error{}",
48
+ :"#REF!" => "RefError{}",
49
+ :"#N/A" => "NAError{}",
50
+ :"#NUM!" => "NumError{}"
51
+ }
52
+
53
+ REVERSE_ERRORS = ERRORS.invert
54
+
55
+ def error(text)
56
+ ERRORS[text.to_sym] || (raise NotSupportedException.new("#{text.inspect} error not recognised"))
57
+ end
58
+
59
+ def boolean_true
60
+ "true"
61
+ end
62
+
63
+ def boolean_false
64
+ "false"
65
+ end
66
+
67
+ end