csv_plus_plus 0.0.2

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 (42) hide show
  1. checksums.yaml +7 -0
  2. data/lib/csv_plus_plus/cell.rb +51 -0
  3. data/lib/csv_plus_plus/code_section.rb +49 -0
  4. data/lib/csv_plus_plus/color.rb +22 -0
  5. data/lib/csv_plus_plus/expand.rb +18 -0
  6. data/lib/csv_plus_plus/google_options.rb +23 -0
  7. data/lib/csv_plus_plus/graph.rb +68 -0
  8. data/lib/csv_plus_plus/language/cell_value.tab.rb +333 -0
  9. data/lib/csv_plus_plus/language/code_section.tab.rb +443 -0
  10. data/lib/csv_plus_plus/language/compiler.rb +170 -0
  11. data/lib/csv_plus_plus/language/entities/boolean.rb +32 -0
  12. data/lib/csv_plus_plus/language/entities/cell_reference.rb +26 -0
  13. data/lib/csv_plus_plus/language/entities/entity.rb +70 -0
  14. data/lib/csv_plus_plus/language/entities/function.rb +33 -0
  15. data/lib/csv_plus_plus/language/entities/function_call.rb +25 -0
  16. data/lib/csv_plus_plus/language/entities/number.rb +34 -0
  17. data/lib/csv_plus_plus/language/entities/runtime_value.rb +27 -0
  18. data/lib/csv_plus_plus/language/entities/string.rb +29 -0
  19. data/lib/csv_plus_plus/language/entities/variable.rb +25 -0
  20. data/lib/csv_plus_plus/language/entities.rb +28 -0
  21. data/lib/csv_plus_plus/language/references.rb +53 -0
  22. data/lib/csv_plus_plus/language/runtime.rb +147 -0
  23. data/lib/csv_plus_plus/language/scope.rb +199 -0
  24. data/lib/csv_plus_plus/language/syntax_error.rb +61 -0
  25. data/lib/csv_plus_plus/lexer/lexer.rb +64 -0
  26. data/lib/csv_plus_plus/lexer/tokenizer.rb +65 -0
  27. data/lib/csv_plus_plus/lexer.rb +14 -0
  28. data/lib/csv_plus_plus/modifier.rb +124 -0
  29. data/lib/csv_plus_plus/modifier.tab.rb +921 -0
  30. data/lib/csv_plus_plus/options.rb +70 -0
  31. data/lib/csv_plus_plus/row.rb +42 -0
  32. data/lib/csv_plus_plus/template.rb +61 -0
  33. data/lib/csv_plus_plus/version.rb +6 -0
  34. data/lib/csv_plus_plus/writer/base_writer.rb +21 -0
  35. data/lib/csv_plus_plus/writer/csv.rb +31 -0
  36. data/lib/csv_plus_plus/writer/excel.rb +13 -0
  37. data/lib/csv_plus_plus/writer/google_sheet_builder.rb +173 -0
  38. data/lib/csv_plus_plus/writer/google_sheets.rb +139 -0
  39. data/lib/csv_plus_plus/writer/open_document.rb +14 -0
  40. data/lib/csv_plus_plus/writer.rb +25 -0
  41. data/lib/csv_plus_plus.rb +20 -0
  42. metadata +83 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 66b51b9e9784f89236625da57b647867b502b50acad209d1e98619fd94fb6f70
4
+ data.tar.gz: f4815f9a10637ee82a4f4f1e32afbd9db47bf728eb3992bb77ce75d9fb460092
5
+ SHA512:
6
+ metadata.gz: 5a983659e3033642f9ff185adca67897a3510578ef765f1cc4f65e8df6ec1f1ecf8b767e6860c3af00c773b6b5d4229bc4699e071a38b04a3f31ec55a225cf76
7
+ data.tar.gz: '0684df3fc8678b6061d572da196bcdfb24f224009c9fa61b64b769178aa18dd6697c7a9f4dba3ab93e15b3a79ecfcd350f23abf1c117bee9026a0b9ba47e6e6c'
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './language/cell_value.tab'
4
+ require_relative './modifier'
5
+
6
+ module CSVPlusPlus
7
+ ##
8
+ # A cell of a template
9
+ class Cell
10
+ attr_accessor :ast, :row_index
11
+ attr_reader :index, :modifier
12
+
13
+ # Parse a +value+ into a Cell object. The +value+ should already have been through
14
+ # a CSV parser
15
+ def self.parse(value, runtime:, modifier:)
16
+ new(value:, row_index: runtime.row_index, index: runtime.cell_index, modifier:).tap do |c|
17
+ c.ast = ::CSVPlusPlus::Language::CellValueParser.new.parse(value, runtime)
18
+ end
19
+ end
20
+
21
+ # initialize
22
+ def initialize(row_index:, index:, value:, modifier:)
23
+ @value = value
24
+ @modifier = modifier
25
+ @index = index
26
+ @row_index = row_index
27
+ end
28
+
29
+ # The value (cleaned up some)
30
+ def value
31
+ return if @value.nil? || @value.strip.empty?
32
+
33
+ @value.strip
34
+ end
35
+
36
+ # to_s
37
+ def to_s
38
+ "Cell(index: #{@index}, row_index: #{@row_index}, value: #{@value}, modifier: #{@modifier})"
39
+ end
40
+
41
+ # A compiled final representation of the cell. This can only happen after all cell have had
42
+ # variables and functions resolved.
43
+ def to_csv
44
+ return value unless @ast
45
+
46
+ # This looks really simple but we're relying on each node of the AST to define #to_s and calling
47
+ # this at the top will recursively print the tree
48
+ "=#{@ast}"
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './language/code_section.tab'
4
+ require_relative './language/entities'
5
+
6
+ module CSVPlusPlus
7
+ ##
8
+ # A representation of the code section part of a template (the variable and function definitions)
9
+ class CodeSection
10
+ attr_reader :functions
11
+ attr_accessor :variables
12
+
13
+ # initialize
14
+ def initialize(variables: {}, functions: {})
15
+ @variables = variables
16
+ @functions = functions
17
+ end
18
+
19
+ # Define a (or re-define an existing) variable
20
+ def def_variable(id, entity)
21
+ @variables[id.to_sym] = entity
22
+ end
23
+
24
+ # Define (or re-define existing) variables
25
+ def def_variables(variables)
26
+ variables.each { |id, entity| def_variable(id, entity) }
27
+ end
28
+
29
+ # Define a (or re-define an existing) function
30
+ def def_function(id, entity)
31
+ @functions[id.to_sym] = entity
32
+ end
33
+
34
+ # Is the variable defined?
35
+ def defined_variable?(var_id)
36
+ @variables.key?(var_id.to_sym)
37
+ end
38
+
39
+ # Is the function defined?
40
+ def defined_function?(fn_id)
41
+ @functions.key?(fn_id.to_sym)
42
+ end
43
+
44
+ # to_s
45
+ def to_s
46
+ "CodeSection(functions: #{@functions}, variables: #{@variables})"
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CSVPlusPlus
4
+ # A color value
5
+ class Color
6
+ attr_reader :red, :green, :blue
7
+
8
+ # create an instance from a string like "#FFF" or "#FFFFFF"
9
+ def initialize(hex_string)
10
+ @red, @green, @blue =
11
+ hex_string
12
+ .gsub(/^#?/, '')
13
+ .match(/(\w\w?)(\w\w?)(\w\w?)/)
14
+ .captures
15
+ .map do |s|
16
+ 255 / (s.length == 2 ? s : s + s).to_i(16)
17
+ rescue ::StandardError
18
+ 0
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CSVPlusPlus
4
+ Expand =
5
+ ::Struct.new(:repetitions) do
6
+ # Does this infinitely expand?
7
+ def infinite?
8
+ repetitions.nil?
9
+ end
10
+
11
+ # to_s
12
+ def to_s
13
+ "Expand #{repetitions || 'infinity'}"
14
+ end
15
+ end
16
+
17
+ public_constant :Expand
18
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CSVPlusPlus
4
+ # The Google-specific options a user can supply
5
+ GoogleOptions =
6
+ ::Struct.new(:sheet_id) do
7
+ # Return a string with a verbose description of what we're doing with the options
8
+ def verbose_summary
9
+ <<~SUMMARY
10
+ ## Google Sheets Options
11
+
12
+ > Sheet ID | #{sheet_id}
13
+ SUMMARY
14
+ end
15
+
16
+ # to_s
17
+ def to_s
18
+ "GoogleOptions(sheet_id: #{sheet_id})"
19
+ end
20
+ end
21
+
22
+ public_constant :GoogleOptions
23
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tsort'
4
+
5
+ module CSVPlusPlus
6
+ ##
7
+ # Graph ordering and searching functions
8
+ module Graph
9
+ # Get a list of all variables references in a given +ast+
10
+ # TODO: this is only used in one place - refactor it
11
+ def self.variable_references(ast, runtime, include_runtime_variables: false)
12
+ depth_first_search(ast) do |node|
13
+ next unless node.variable?
14
+
15
+ node.id if !runtime.runtime_variable?(node.id) || include_runtime_variables
16
+ end
17
+ end
18
+
19
+ # Create a dependency graph of +variables+
20
+ def self.dependency_graph(variables, runtime)
21
+ ::CSVPlusPlus::Graph::DependencyGraph[
22
+ variables.map { |var_id, ast| [var_id, variable_references(ast, runtime)] }
23
+ ]
24
+ end
25
+
26
+ # Perform a topological sort on a +DependencyGraph+. A toplogical sort is noteworthy
27
+ # because it will give us the order in which we need to resolve our variable dependencies.
28
+ #
29
+ # Given this dependency graph:
30
+ #
31
+ # { a: [b c], b: [c], c: [d], d: [] }
32
+ #
33
+ # it will return:
34
+ #
35
+ # [d, c, b, a]
36
+ #
37
+ def self.topological_sort(dependencies)
38
+ dependencies.tsort
39
+ end
40
+
41
+ # Do a DFS on an AST starting at +node+
42
+ def self.depth_first_search(node, accum = [], &)
43
+ ret = yield(node)
44
+ accum << ret unless ret.nil?
45
+
46
+ return accum unless node.function_call?
47
+
48
+ node.arguments.each { |n| depth_first_search(n, accum, &) }
49
+ accum
50
+ end
51
+
52
+ # A dependency graph represented as a +Hash+ which will be used by our +topological_sort+ function
53
+ class DependencyGraph < Hash
54
+ include ::TSort
55
+ alias tsort_each_node each_key
56
+
57
+ # create a +DependencyGraph+ from a +Hash+
58
+ def self.from_hash(hash)
59
+ self[hash.map { |k, v| [k, v] }]
60
+ end
61
+
62
+ # sort each child
63
+ def tsort_each_child(node, &)
64
+ fetch(node).each(&)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,333 @@
1
+ #
2
+ # DO NOT MODIFY!!!!
3
+ # This file is automatically generated by Racc 1.6.2
4
+ # from Racc grammar file "".
5
+ #
6
+
7
+ require 'racc/parser.rb'
8
+
9
+ require_relative '../lexer'
10
+
11
+ module CSVPlusPlus
12
+ module Language
13
+ class CellValueParser < Racc::Parser
14
+
15
+ module_eval(<<'...end cell_value.y/module_eval...', 'cell_value.y', 48)
16
+ include ::CSVPlusPlus::Lexer
17
+
18
+ protected
19
+
20
+ def anything_to_parse?(input)
21
+ input.strip.start_with?('=')
22
+ end
23
+
24
+ def parse_subject
25
+ 'cell value'
26
+ end
27
+
28
+ def return_value
29
+ @ast
30
+ end
31
+
32
+ def tokenizer(input)
33
+ ::CSVPlusPlus::Lexer::Tokenizer.new(
34
+ catchall: /[\(\)\/\*\+\-,=&]/,
35
+ ignore: /\s+/,
36
+ input:,
37
+ tokens: [
38
+ [/true/i, :TRUE],
39
+ [/false/i, :FALSE],
40
+ [/"(?:[^"\\]|\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4}))*"/, :STRING],
41
+ [/-?[\d.]+/, :NUMBER],
42
+ [/\$\$/, :VAR_REF],
43
+ [/[\$\w_]+/, :ID]
44
+ ]
45
+ )
46
+ end
47
+ ...end cell_value.y/module_eval...
48
+ ##### State transition tables begin ###
49
+
50
+ racc_action_table = [
51
+ 33, 16, 17, 20, 18, 19, 15, 7, 16, 17,
52
+ 20, 18, 19, 7, 34, 12, 13, 10, 9, 11,
53
+ 8, 12, 13, 10, 9, 11, 8, 7, 16, 17,
54
+ 20, 18, 19, 7, 2, 12, 13, 10, 9, 11,
55
+ 8, 12, 13, 10, 9, 11, 8, 7, 16, 17,
56
+ 20, 3, 14, 7, 22, 12, 13, 10, 9, 11,
57
+ 8, 12, 13, 10, 9, 11, 8, 7, 16, 17,
58
+ 20, 23, 16, 7, 31, 12, 13, 10, 9, 11,
59
+ 8, 12, 13, 10, 9, 11, 8, 7, 29, 16,
60
+ 17, 20, 18, 19, 16, 12, 13, 10, 9, 11,
61
+ 8 ]
62
+
63
+ racc_action_check = [
64
+ 30, 4, 4, 4, 4, 4, 4, 2, 32, 32,
65
+ 32, 32, 32, 7, 30, 2, 2, 2, 2, 2,
66
+ 2, 7, 7, 7, 7, 7, 7, 16, 35, 35,
67
+ 35, 35, 35, 17, 0, 16, 16, 16, 16, 16,
68
+ 16, 17, 17, 17, 17, 17, 17, 18, 26, 26,
69
+ 26, 1, 3, 19, 8, 18, 18, 18, 18, 18,
70
+ 18, 19, 19, 19, 19, 19, 19, 20, 27, 27,
71
+ 27, 13, 25, 23, 23, 20, 20, 20, 20, 20,
72
+ 20, 23, 23, 23, 23, 23, 23, 34, 21, 21,
73
+ 21, 21, 21, 21, 28, 34, 34, 34, 34, 34,
74
+ 34 ]
75
+
76
+ racc_action_pointer = [
77
+ 18, 51, 5, 52, -3, nil, nil, 11, 43, nil,
78
+ nil, nil, nil, 69, nil, nil, 25, 31, 45, 51,
79
+ 65, 85, nil, 71, nil, 68, 44, 64, 90, nil,
80
+ -3, nil, 4, nil, 85, 24 ]
81
+
82
+ racc_action_default = [
83
+ -20, -20, -20, -20, -20, -2, -3, -20, -20, -6,
84
+ -7, -8, -9, -10, 36, -1, -20, -20, -20, -20,
85
+ -20, -20, -5, -20, -15, -16, -17, -18, -19, -4,
86
+ -20, -12, -14, -11, -20, -13 ]
87
+
88
+ racc_goto_table = [
89
+ 4, 1, 30, nil, nil, 21, nil, nil, nil, nil,
90
+ nil, nil, nil, nil, 24, 25, 26, 27, 28, nil,
91
+ nil, 32, nil, nil, nil, nil, nil, nil, nil, nil,
92
+ nil, nil, 35 ]
93
+
94
+ racc_goto_check = [
95
+ 2, 1, 5, nil, nil, 2, nil, nil, nil, nil,
96
+ nil, nil, nil, nil, 2, 2, 2, 2, 2, nil,
97
+ nil, 2, nil, nil, nil, nil, nil, nil, nil, nil,
98
+ nil, nil, 2 ]
99
+
100
+ racc_goto_pointer = [
101
+ nil, 1, -2, nil, nil, -21 ]
102
+
103
+ racc_goto_default = [
104
+ nil, nil, nil, 5, 6, nil ]
105
+
106
+ racc_reduce_table = [
107
+ 0, 0, :racc_error,
108
+ 3, 19, :_reduce_1,
109
+ 1, 20, :_reduce_none,
110
+ 1, 20, :_reduce_none,
111
+ 3, 20, :_reduce_4,
112
+ 2, 20, :_reduce_5,
113
+ 1, 20, :_reduce_6,
114
+ 1, 20, :_reduce_7,
115
+ 1, 20, :_reduce_8,
116
+ 1, 20, :_reduce_9,
117
+ 1, 20, :_reduce_10,
118
+ 4, 21, :_reduce_11,
119
+ 3, 21, :_reduce_12,
120
+ 3, 23, :_reduce_13,
121
+ 1, 23, :_reduce_14,
122
+ 3, 22, :_reduce_15,
123
+ 3, 22, :_reduce_16,
124
+ 3, 22, :_reduce_17,
125
+ 3, 22, :_reduce_18,
126
+ 3, 22, :_reduce_19 ]
127
+
128
+ racc_reduce_n = 20
129
+
130
+ racc_shift_n = 36
131
+
132
+ racc_token_table = {
133
+ false => 0,
134
+ :error => 1,
135
+ "(" => 2,
136
+ ")" => 3,
137
+ "&" => 4,
138
+ "*" => 5,
139
+ "/" => 6,
140
+ "+" => 7,
141
+ "-" => 8,
142
+ :EOL => 9,
143
+ :FALSE => 10,
144
+ :ID => 11,
145
+ :NUMBER => 12,
146
+ :STRING => 13,
147
+ :TRUE => 14,
148
+ :VAR_REF => 15,
149
+ "=" => 16,
150
+ "," => 17 }
151
+
152
+ racc_nt_base = 18
153
+
154
+ racc_use_result_var = true
155
+
156
+ Racc_arg = [
157
+ racc_action_table,
158
+ racc_action_check,
159
+ racc_action_default,
160
+ racc_action_pointer,
161
+ racc_goto_table,
162
+ racc_goto_check,
163
+ racc_goto_default,
164
+ racc_goto_pointer,
165
+ racc_nt_base,
166
+ racc_reduce_table,
167
+ racc_token_table,
168
+ racc_shift_n,
169
+ racc_reduce_n,
170
+ racc_use_result_var ]
171
+
172
+ Racc_token_to_s_table = [
173
+ "$end",
174
+ "error",
175
+ "\"(\"",
176
+ "\")\"",
177
+ "\"&\"",
178
+ "\"*\"",
179
+ "\"/\"",
180
+ "\"+\"",
181
+ "\"-\"",
182
+ "EOL",
183
+ "FALSE",
184
+ "ID",
185
+ "NUMBER",
186
+ "STRING",
187
+ "TRUE",
188
+ "VAR_REF",
189
+ "\"=\"",
190
+ "\",\"",
191
+ "$start",
192
+ "cell_value",
193
+ "exp",
194
+ "fn_call",
195
+ "infix_fn_call",
196
+ "fn_call_args" ]
197
+
198
+ Racc_debug_parser = false
199
+
200
+ ##### State transition tables end #####
201
+
202
+ # reduce 0 omitted
203
+
204
+ module_eval(<<'.,.,', 'cell_value.y', 17)
205
+ def _reduce_1(val, _values, result)
206
+ @ast = val[1]
207
+ result
208
+ end
209
+ .,.,
210
+
211
+ # reduce 2 omitted
212
+
213
+ # reduce 3 omitted
214
+
215
+ module_eval(<<'.,.,', 'cell_value.y', 21)
216
+ def _reduce_4(val, _values, result)
217
+ result = val[1]
218
+ result
219
+ end
220
+ .,.,
221
+
222
+ module_eval(<<'.,.,', 'cell_value.y', 22)
223
+ def _reduce_5(val, _values, result)
224
+ result = e(:variable, val[1])
225
+ result
226
+ end
227
+ .,.,
228
+
229
+ module_eval(<<'.,.,', 'cell_value.y', 23)
230
+ def _reduce_6(val, _values, result)
231
+ result = e(:string, val[0])
232
+ result
233
+ end
234
+ .,.,
235
+
236
+ module_eval(<<'.,.,', 'cell_value.y', 24)
237
+ def _reduce_7(val, _values, result)
238
+ result = e(:number, val[0])
239
+ result
240
+ end
241
+ .,.,
242
+
243
+ module_eval(<<'.,.,', 'cell_value.y', 25)
244
+ def _reduce_8(val, _values, result)
245
+ result = e(:boolean, true)
246
+ result
247
+ end
248
+ .,.,
249
+
250
+ module_eval(<<'.,.,', 'cell_value.y', 26)
251
+ def _reduce_9(val, _values, result)
252
+ result = e(:boolean, false)
253
+ result
254
+ end
255
+ .,.,
256
+
257
+ module_eval(<<'.,.,', 'cell_value.y', 27)
258
+ def _reduce_10(val, _values, result)
259
+ result = e(:cell_reference, val[0])
260
+ result
261
+ end
262
+ .,.,
263
+
264
+ module_eval(<<'.,.,', 'cell_value.y', 29)
265
+ def _reduce_11(val, _values, result)
266
+ result = e(:function_call, val[0], val[2])
267
+ result
268
+ end
269
+ .,.,
270
+
271
+ module_eval(<<'.,.,', 'cell_value.y', 30)
272
+ def _reduce_12(val, _values, result)
273
+ result = e(:function_call, val[0], [])
274
+ result
275
+ end
276
+ .,.,
277
+
278
+ module_eval(<<'.,.,', 'cell_value.y', 32)
279
+ def _reduce_13(val, _values, result)
280
+ result = val[0] << val[2]
281
+ result
282
+ end
283
+ .,.,
284
+
285
+ module_eval(<<'.,.,', 'cell_value.y', 33)
286
+ def _reduce_14(val, _values, result)
287
+ result = [val[0]]
288
+ result
289
+ end
290
+ .,.,
291
+
292
+ module_eval(<<'.,.,', 'cell_value.y', 35)
293
+ def _reduce_15(val, _values, result)
294
+ result = e(:function_call, :concat, [val[0], val[2]])
295
+ result
296
+ end
297
+ .,.,
298
+
299
+ module_eval(<<'.,.,', 'cell_value.y', 36)
300
+ def _reduce_16(val, _values, result)
301
+ result = e(:function_call, :multiply, [val[0], val[2]])
302
+ result
303
+ end
304
+ .,.,
305
+
306
+ module_eval(<<'.,.,', 'cell_value.y', 37)
307
+ def _reduce_17(val, _values, result)
308
+ result = e(:function_call, :add, [val[0], val[2]])
309
+ result
310
+ end
311
+ .,.,
312
+
313
+ module_eval(<<'.,.,', 'cell_value.y', 38)
314
+ def _reduce_18(val, _values, result)
315
+ result = e(:function_call, :minus, [val[0], val[2]])
316
+ result
317
+ end
318
+ .,.,
319
+
320
+ module_eval(<<'.,.,', 'cell_value.y', 39)
321
+ def _reduce_19(val, _values, result)
322
+ result = e(:function_call, :divide, [val[0], val[2]])
323
+ result
324
+ end
325
+ .,.,
326
+
327
+ def _reduce_none(val, _values, result)
328
+ val[0]
329
+ end
330
+
331
+ end # class CellValueParser
332
+ end # module Language
333
+ end # module CSVPlusPlus