csv_plus_plus 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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