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.
- checksums.yaml +5 -5
- data/README.md +67 -34
- data/bin/excel_to_c +8 -78
- data/bin/excel_to_go +41 -0
- data/bin/excel_to_ruby +2 -69
- data/src/commands.rb +2 -0
- data/src/commands/common_command_line_options.rb +81 -0
- data/src/commands/excel_to_c.rb +3 -0
- data/src/commands/excel_to_go.rb +91 -0
- data/src/commands/excel_to_x.rb +77 -11
- data/src/compile.rb +1 -0
- data/src/compile/c/a.out +0 -0
- data/src/compile/c/a.out.dSYM/Contents/Resources/DWARF/a.out +0 -0
- data/src/compile/c/compile_to_c.rb +2 -0
- data/src/compile/c/excel_to_c_runtime.c +691 -145
- data/src/compile/c/excel_to_c_runtime_test.c +226 -20
- data/src/compile/c/map_formulae_to_c.rb +62 -23
- data/src/compile/c/run_c_unit_tests +3 -0
- data/src/compile/cd.rb +6 -0
- data/src/compile/go.rb +3 -0
- data/src/compile/go/compile_to_go.rb +85 -0
- data/src/compile/go/compile_to_go_test.rb +73 -0
- data/src/compile/go/excel.go +171 -0
- data/src/compile/go/excel_test.go +54 -0
- data/src/compile/go/map_values_to_go.rb +67 -0
- data/src/compile/ruby/map_formulae_to_ruby.rb +30 -12
- data/src/excel/excel_functions.rb +26 -1
- data/src/excel/excel_functions/ceiling.rb +23 -0
- data/src/excel/excel_functions/countif.rb +15 -0
- data/src/excel/excel_functions/countifs.rb +10 -0
- data/src/excel/excel_functions/floor.rb +14 -0
- data/src/excel/excel_functions/hyperlink.rb +9 -0
- data/src/excel/excel_functions/na.rb +7 -0
- data/src/excel/excel_functions/not.rb +13 -0
- data/src/excel/excel_functions/or.rb +30 -0
- data/src/excel/excel_functions/product.rb +8 -0
- data/src/excel/excel_functions/rate.rb +16 -0
- data/src/excel/excel_functions/replace.rb +13 -0
- data/src/excel/excel_functions/scurve.rb +73 -0
- data/src/excel/excel_functions/sqrt.rb +11 -0
- data/src/excel/excel_functions/string_argument.rb +37 -0
- data/src/excel/excel_functions/sumifs.rb +19 -8
- data/src/excel/excel_functions/text.rb +3 -3
- data/src/excel/formula_peg.rb +1 -1
- data/src/excel/formula_peg.txt +2 -3
- data/src/excel/table.rb +15 -15
- data/src/excel_to_code.rb +1 -4
- data/src/extract/extract_data_from_worksheet.rb +8 -1
- data/src/rewrite/ast_expand_array_formulae.rb +4 -0
- data/src/rewrite/caching_formula_parser.rb +16 -11
- data/src/simplify.rb +1 -0
- data/src/simplify/inline_formulae.rb +16 -0
- data/src/simplify/replace_arithmetic_on_ranges.rb +14 -1
- data/src/simplify/replace_arrays_with_single_cells.rb +42 -15
- data/src/simplify/replace_cell_addresses_with_references.rb +70 -0
- data/src/simplify/replace_column_with_column_number.rb +8 -1
- data/src/simplify/replace_table_references.rb +40 -19
- data/src/simplify/simplify_arithmetic.rb +15 -10
- data/src/version.rb +4 -0
- metadata +115 -43
- data/TODO +0 -25
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,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
|