csv_plus_plus 0.1.3 → 0.2.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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -3
  3. data/docs/CHANGELOG.md +18 -0
  4. data/lib/csv_plus_plus/a1_reference.rb +202 -0
  5. data/lib/csv_plus_plus/benchmarked_compiler.rb +3 -3
  6. data/lib/csv_plus_plus/cell.rb +1 -35
  7. data/lib/csv_plus_plus/cli.rb +43 -80
  8. data/lib/csv_plus_plus/cli_flag.rb +77 -70
  9. data/lib/csv_plus_plus/color.rb +1 -1
  10. data/lib/csv_plus_plus/compiler.rb +31 -21
  11. data/lib/csv_plus_plus/entities/ast_builder.rb +11 -4
  12. data/lib/csv_plus_plus/entities/boolean.rb +16 -9
  13. data/lib/csv_plus_plus/entities/builtins.rb +68 -40
  14. data/lib/csv_plus_plus/entities/date.rb +14 -11
  15. data/lib/csv_plus_plus/entities/entity.rb +11 -29
  16. data/lib/csv_plus_plus/entities/entity_with_arguments.rb +18 -31
  17. data/lib/csv_plus_plus/entities/function.rb +22 -11
  18. data/lib/csv_plus_plus/entities/function_call.rb +35 -11
  19. data/lib/csv_plus_plus/entities/has_identifier.rb +19 -0
  20. data/lib/csv_plus_plus/entities/number.rb +15 -10
  21. data/lib/csv_plus_plus/entities/reference.rb +77 -0
  22. data/lib/csv_plus_plus/entities/runtime_value.rb +36 -23
  23. data/lib/csv_plus_plus/entities/string.rb +13 -10
  24. data/lib/csv_plus_plus/entities.rb +2 -18
  25. data/lib/csv_plus_plus/error/cli_error.rb +17 -0
  26. data/lib/csv_plus_plus/error/compiler_error.rb +17 -0
  27. data/lib/csv_plus_plus/error/error.rb +18 -5
  28. data/lib/csv_plus_plus/error/formula_syntax_error.rb +12 -13
  29. data/lib/csv_plus_plus/error/modifier_syntax_error.rb +10 -36
  30. data/lib/csv_plus_plus/error/modifier_validation_error.rb +6 -32
  31. data/lib/csv_plus_plus/error/positional_error.rb +15 -0
  32. data/lib/csv_plus_plus/error/writer_error.rb +1 -1
  33. data/lib/csv_plus_plus/error.rb +4 -1
  34. data/lib/csv_plus_plus/error_formatter.rb +111 -0
  35. data/lib/csv_plus_plus/google_api_client.rb +18 -8
  36. data/lib/csv_plus_plus/lexer/racc_lexer.rb +144 -0
  37. data/lib/csv_plus_plus/lexer/tokenizer.rb +53 -17
  38. data/lib/csv_plus_plus/lexer.rb +40 -1
  39. data/lib/csv_plus_plus/modifier/data_validation.rb +1 -1
  40. data/lib/csv_plus_plus/modifier/expand.rb +17 -0
  41. data/lib/csv_plus_plus/modifier.rb +6 -1
  42. data/lib/csv_plus_plus/options/file_options.rb +49 -0
  43. data/lib/csv_plus_plus/options/google_sheets_options.rb +42 -0
  44. data/lib/csv_plus_plus/options/options.rb +102 -0
  45. data/lib/csv_plus_plus/options.rb +22 -110
  46. data/lib/csv_plus_plus/parser/cell_value.tab.rb +65 -66
  47. data/lib/csv_plus_plus/parser/code_section.tab.rb +92 -84
  48. data/lib/csv_plus_plus/parser/modifier.tab.rb +40 -30
  49. data/lib/csv_plus_plus/reader/csv.rb +50 -0
  50. data/lib/csv_plus_plus/reader/google_sheets.rb +129 -0
  51. data/lib/csv_plus_plus/reader/reader.rb +27 -0
  52. data/lib/csv_plus_plus/reader/rubyxl.rb +37 -0
  53. data/lib/csv_plus_plus/reader.rb +14 -0
  54. data/lib/csv_plus_plus/runtime/graph.rb +6 -6
  55. data/lib/csv_plus_plus/runtime/{position_tracker.rb → position.rb} +16 -5
  56. data/lib/csv_plus_plus/runtime/references.rb +32 -27
  57. data/lib/csv_plus_plus/runtime/runtime.rb +73 -67
  58. data/lib/csv_plus_plus/runtime/scope.rb +280 -0
  59. data/lib/csv_plus_plus/runtime.rb +9 -9
  60. data/lib/csv_plus_plus/source_code.rb +14 -9
  61. data/lib/csv_plus_plus/template.rb +17 -12
  62. data/lib/csv_plus_plus/version.rb +1 -1
  63. data/lib/csv_plus_plus/writer/csv.rb +32 -5
  64. data/lib/csv_plus_plus/writer/excel.rb +19 -6
  65. data/lib/csv_plus_plus/writer/file_backer_upper.rb +27 -14
  66. data/lib/csv_plus_plus/writer/google_sheets.rb +23 -129
  67. data/lib/csv_plus_plus/writer/{google_sheet_builder.rb → google_sheets_builder.rb} +39 -55
  68. data/lib/csv_plus_plus/writer/merger.rb +56 -0
  69. data/lib/csv_plus_plus/writer/open_document.rb +16 -2
  70. data/lib/csv_plus_plus/writer/rubyxl_builder.rb +68 -43
  71. data/lib/csv_plus_plus/writer/writer.rb +42 -0
  72. data/lib/csv_plus_plus/writer.rb +58 -19
  73. data/lib/csv_plus_plus.rb +26 -14
  74. metadata +43 -18
  75. data/lib/csv_plus_plus/entities/cell_reference.rb +0 -231
  76. data/lib/csv_plus_plus/entities/variable.rb +0 -37
  77. data/lib/csv_plus_plus/error/syntax_error.rb +0 -71
  78. data/lib/csv_plus_plus/google_options.rb +0 -32
  79. data/lib/csv_plus_plus/lexer/lexer.rb +0 -89
  80. data/lib/csv_plus_plus/runtime/can_define_references.rb +0 -87
  81. data/lib/csv_plus_plus/runtime/can_resolve_references.rb +0 -209
  82. data/lib/csv_plus_plus/writer/base_writer.rb +0 -45
@@ -6,33 +6,46 @@
6
6
 
7
7
  require 'racc/parser.rb'
8
8
 
9
- require_relative '../lexer'
9
+ require_relative '../lexer/racc_lexer'
10
10
  require_relative '../entities/ast_builder'
11
11
 
12
12
  module CSVPlusPlus
13
13
  module Parser
14
14
  class CodeSection < Racc::Parser
15
15
 
16
- module_eval(<<'...end code_section.y/module_eval...', 'code_section.y', 69)
17
- include ::CSVPlusPlus::Lexer
16
+ module_eval(<<'...end code_section.y/module_eval...', 'code_section.y', 68)
17
+ extend ::T::Sig
18
+ extend ::T::Generic
19
+ include ::CSVPlusPlus::Lexer::RaccLexer
18
20
  include ::CSVPlusPlus::Entities::ASTBuilder
19
21
 
22
+ ReturnType = type_member {{ fixed: ::T.nilable(::String) }}
23
+
24
+ sig { params(scope: ::CSVPlusPlus::Runtime::Scope).void }
25
+ def initialize(scope)
26
+ super()
27
+ @scope = scope
28
+ end
29
+
20
30
  protected
21
31
 
32
+ sig { override.params(input: ::String).returns(::T::Boolean) }
22
33
  def anything_to_parse?(input)
23
34
  @rest = input.strip
24
35
 
25
36
  return !@rest.index(::CSVPlusPlus::Lexer::END_OF_CODE_SECTION).nil?
26
37
  end
27
38
 
39
+ sig { override.returns(::String) }
28
40
  def parse_subject
29
41
  'code section'
30
42
  end
31
43
 
44
+ sig { override.returns(::CSVPlusPlus::Lexer::Tokenizer) }
32
45
  def tokenizer
33
46
  ::CSVPlusPlus::Lexer::Tokenizer.new(
34
47
  catchall: /[\{\}\(\),]/, # TODO: do I even need this (oh I think brackets are for arrays
35
- ignore: /\s+|\#[^\n]+\n/,
48
+ ignore: /\s+|\#.*/,
36
49
  stop_fn: lambda do |scanner|
37
50
  return false unless scanner.scan(/#{::CSVPlusPlus::Lexer::END_OF_CODE_SECTION}/)
38
51
 
@@ -41,85 +54,88 @@ module_eval(<<'...end code_section.y/module_eval...', 'code_section.y', 69)
41
54
  true
42
55
  end,
43
56
  tokens: [
44
- [/:=/, :ASSIGN],
45
- [/def/, :FN_DEF],
46
- TOKEN_LIBRARY[:TRUE],
47
- TOKEN_LIBRARY[:FALSE],
48
- TOKEN_LIBRARY[:NUMBER],
49
- TOKEN_LIBRARY[:STRING],
50
- TOKEN_LIBRARY[:INFIX_OP],
51
- TOKEN_LIBRARY[:VAR_REF],
52
- TOKEN_LIBRARY[:ID]
57
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /:=/, token: :ASSIGN),
58
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /\bdef\b/, token: :FN_DEF),
59
+ ::CSVPlusPlus::Lexer::TOKEN_LIBRARY[:TRUE],
60
+ ::CSVPlusPlus::Lexer::TOKEN_LIBRARY[:FALSE],
61
+ ::CSVPlusPlus::Lexer::TOKEN_LIBRARY[:NUMBER],
62
+ ::CSVPlusPlus::Lexer::TOKEN_LIBRARY[:STRING],
63
+ ::CSVPlusPlus::Lexer::TOKEN_LIBRARY[:INFIX_OP],
64
+ ::CSVPlusPlus::Lexer::TOKEN_LIBRARY[:REF]
53
65
  ],
54
66
  )
55
67
  end
56
68
 
69
+ sig { override.returns(ReturnType) }
57
70
  def return_value
58
71
  @rest
59
72
  end
60
73
 
61
74
  private
62
75
 
76
+ sig do
77
+ params(id: ::Symbol, arguments: ::T::Array[::Symbol], body: ::CSVPlusPlus::Entities::Entity)
78
+ .returns(::CSVPlusPlus::Entities::Entity)
79
+ end
63
80
  def def_function(id, arguments, body)
64
- fn_def = function(id.to_sym, arguments, body)
65
- @runtime.def_function(fn_def.id, fn_def)
81
+ @scope.def_function(id, function(id, arguments, body))
66
82
  end
67
83
 
84
+ sig do
85
+ params(id: ::Symbol, ast: ::CSVPlusPlus::Entities::Entity)
86
+ .returns(::CSVPlusPlus::Entities::Entity)
87
+ end
68
88
  def def_variable(id, ast)
69
- @runtime.def_variable(id.to_sym, ast)
89
+ @scope.def_variable(id, ast)
70
90
  end
71
91
  ...end code_section.y/module_eval...
72
92
  ##### State transition tables begin ###
73
93
 
74
94
  racc_action_table = [
75
- 20, 38, 9, 12, 13, 14, 16, 20, 31, 33,
76
- 34, 31, 42, 31, 20, 31, 29, 25, 26, 31,
77
- 23, 22, 24, 21, 25, 26, 20, 23, 22, 24,
78
- 21, 25, 26, 30, 23, 22, 24, 21, 20, 41,
79
- 31, nil, 35, 25, 26, 20, 23, 22, 24, 21,
80
- 3, 10, nil, 7, 7, 25, 26, 36, 23, 22,
81
- 24, 21, 25, 26, 43, 23, 22, 24, 21, 8,
82
- 8, nil, nil, nil, nil, nil, nil, nil, nil, 44 ]
95
+ 20, 33, 9, 12, 13, 28, 20, 14, 16, 30,
96
+ 32, 30, 20, 40, 30, 30, 34, 24, 20, 22,
97
+ 25, 21, 23, 24, 29, 22, 25, 21, 23, 24,
98
+ 20, 22, 25, 21, 23, 24, 36, 22, 25, 21,
99
+ 23, 20, 39, 3, 41, 10, 7, 24, 7, 22,
100
+ 25, 21, 23, 30, 30, nil, nil, nil, 24, 42,
101
+ 22, 25, 21, 23, 8, nil, 8 ]
83
102
 
84
103
  racc_action_check = [
85
- 13, 32, 1, 7, 8, 9, 12, 15, 17, 21,
86
- 26, 27, 36, 37, 20, 39, 16, 13, 13, 32,
87
- 13, 13, 13, 13, 15, 15, 31, 15, 15, 15,
88
- 15, 20, 20, 16, 20, 20, 20, 20, 34, 34,
89
- 45, nil, 28, 31, 31, 44, 31, 31, 31, 31,
90
- 0, 2, nil, 0, 2, 34, 34, 28, 34, 34,
91
- 34, 34, 44, 44, 40, 44, 44, 44, 44, 0,
92
- 2, nil, nil, nil, nil, nil, nil, nil, nil, 40 ]
104
+ 13, 27, 1, 7, 8, 16, 15, 9, 12, 17,
105
+ 25, 26, 20, 34, 35, 37, 27, 13, 30, 13,
106
+ 13, 13, 13, 15, 16, 15, 15, 15, 15, 20,
107
+ 42, 20, 20, 20, 20, 30, 31, 30, 30, 30,
108
+ 30, 32, 32, 0, 38, 2, 0, 42, 2, 42,
109
+ 42, 42, 42, 31, 43, nil, nil, nil, 32, 38,
110
+ 32, 32, 32, 32, 0, nil, 2 ]
93
111
 
94
112
  racc_action_pointer = [
95
- 48, 2, 49, nil, nil, nil, nil, -18, -2, 5,
96
- nil, nil, 3, -3, nil, 4, 12, -14, nil, nil,
97
- 11, -12, nil, nil, nil, nil, 7, -11, 38, nil,
98
- nil, 23, -3, nil, 35, nil, -9, -9, nil, -7,
99
- 60, nil, nil, nil, 42, 18 ]
113
+ 41, 2, 43, nil, nil, nil, nil, -20, -2, 7,
114
+ nil, nil, 5, -3, nil, 3, 1, -12, nil, nil,
115
+ 9, nil, nil, nil, nil, 7, -10, -3, nil, nil,
116
+ 15, 32, 38, nil, -10, -7, nil, -6, 40, nil,
117
+ nil, nil, 27, 33 ]
100
118
 
101
119
  racc_action_default = [
102
- -27, -27, -27, -2, -4, -5, -6, -27, -27, -27,
103
- -1, -3, -27, -27, 46, -27, -27, -12, -13, -14,
104
- -27, -27, -17, -18, -19, -20, -21, -7, -27, -9,
105
- -11, -27, -27, -16, -27, -8, -27, -22, -15, -26,
106
- -27, -24, -10, -23, -27, -25 ]
120
+ -26, -26, -26, -2, -4, -5, -6, -26, -26, -26,
121
+ -1, -3, -26, -26, 44, -26, -26, -12, -13, -14,
122
+ -26, -16, -17, -18, -19, -20, -7, -26, -9, -11,
123
+ -26, -26, -26, -8, -26, -21, -15, -25, -26, -23,
124
+ -10, -22, -26, -24 ]
107
125
 
108
126
  racc_goto_table = [
109
- 17, 4, 27, 11, 1, 2, 15, 32, 28, 40,
110
- nil, nil, nil, nil, nil, nil, nil, nil, 37, nil,
111
- nil, 39, nil, nil, nil, nil, nil, nil, nil, nil,
112
- nil, 45 ]
127
+ 17, 4, 26, 11, 1, 2, 15, 31, 27, 38,
128
+ nil, nil, nil, nil, nil, nil, nil, 35, nil, 37,
129
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, 43 ]
113
130
 
114
131
  racc_goto_check = [
115
132
  7, 3, 7, 3, 1, 2, 6, 7, 8, 11,
116
- nil, nil, nil, nil, nil, nil, nil, nil, 7, nil,
117
- nil, 7, nil, nil, nil, nil, nil, nil, nil, nil,
118
- nil, 7 ]
133
+ nil, nil, nil, nil, nil, nil, nil, 7, nil, 7,
134
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, 7 ]
119
135
 
120
136
  racc_goto_pointer = [
121
137
  nil, 4, 5, 1, nil, nil, -6, -13, -8, nil,
122
- nil, -25 ]
138
+ nil, -23 ]
123
139
 
124
140
  racc_goto_default = [
125
141
  nil, nil, nil, nil, 5, 6, nil, nil, nil, 18,
@@ -142,21 +158,20 @@ racc_reduce_table = [
142
158
  1, 34, :_reduce_none,
143
159
  1, 34, :_reduce_none,
144
160
  3, 34, :_reduce_15,
145
- 2, 34, :_reduce_16,
161
+ 1, 34, :_reduce_16,
146
162
  1, 34, :_reduce_17,
147
163
  1, 34, :_reduce_18,
148
164
  1, 34, :_reduce_19,
149
165
  1, 34, :_reduce_20,
150
- 1, 34, :_reduce_21,
151
- 3, 37, :_reduce_22,
152
- 4, 36, :_reduce_23,
153
- 3, 36, :_reduce_24,
154
- 3, 38, :_reduce_25,
155
- 1, 38, :_reduce_26 ]
166
+ 3, 37, :_reduce_21,
167
+ 4, 36, :_reduce_22,
168
+ 3, 36, :_reduce_23,
169
+ 3, 38, :_reduce_24,
170
+ 1, 38, :_reduce_25 ]
156
171
 
157
- racc_reduce_n = 27
172
+ racc_reduce_n = 26
158
173
 
159
- racc_shift_n = 46
174
+ racc_shift_n = 44
160
175
 
161
176
  racc_token_table = {
162
177
  false => 0,
@@ -180,9 +195,9 @@ racc_token_table = {
180
195
  "<>" => 18,
181
196
  "," => 19,
182
197
  :FALSE => 20,
183
- :ID => 21,
184
- :INFIX_OP => 22,
185
- :NUMBER => 23,
198
+ :INFIX_OP => 21,
199
+ :NUMBER => 22,
200
+ :REF => 23,
186
201
  :STRING => 24,
187
202
  :TRUE => 25,
188
203
  :VAR_REF => 26 }
@@ -229,9 +244,9 @@ Racc_token_to_s_table = [
229
244
  "\"<>\"",
230
245
  "\",\"",
231
246
  "FALSE",
232
- "ID",
233
247
  "INFIX_OP",
234
248
  "NUMBER",
249
+ "REF",
235
250
  "STRING",
236
251
  "TRUE",
237
252
  "VAR_REF",
@@ -268,7 +283,7 @@ Racc_debug_parser = false
268
283
 
269
284
  module_eval(<<'.,.,', 'code_section.y', 33)
270
285
  def _reduce_7(val, _values, result)
271
- def_function(val[1], val[2], val[3])
286
+ def_function(val[1].to_sym, val[2], val[3])
272
287
  result
273
288
  end
274
289
  .,.,
@@ -303,7 +318,7 @@ module_eval(<<'.,.,', 'code_section.y', 39)
303
318
 
304
319
  module_eval(<<'.,.,', 'code_section.y', 41)
305
320
  def _reduce_12(val, _values, result)
306
- def_variable(val[0], val[2])
321
+ def_variable(val[0].to_sym, val[2])
307
322
  result
308
323
  end
309
324
  .,.,
@@ -321,76 +336,69 @@ module_eval(<<'.,.,', 'code_section.y', 45)
321
336
 
322
337
  module_eval(<<'.,.,', 'code_section.y', 46)
323
338
  def _reduce_16(val, _values, result)
324
- result = variable(val[1].to_sym)
339
+ result = string(val[0])
325
340
  result
326
341
  end
327
342
  .,.,
328
343
 
329
344
  module_eval(<<'.,.,', 'code_section.y', 47)
330
345
  def _reduce_17(val, _values, result)
331
- result = string(val[0])
346
+ result = number(val[0])
332
347
  result
333
348
  end
334
349
  .,.,
335
350
 
336
351
  module_eval(<<'.,.,', 'code_section.y', 48)
337
352
  def _reduce_18(val, _values, result)
338
- result = number(val[0])
353
+ result = boolean(true)
339
354
  result
340
355
  end
341
356
  .,.,
342
357
 
343
358
  module_eval(<<'.,.,', 'code_section.y', 49)
344
359
  def _reduce_19(val, _values, result)
345
- result = boolean(true)
360
+ result = boolean(false)
346
361
  result
347
362
  end
348
363
  .,.,
349
364
 
350
365
  module_eval(<<'.,.,', 'code_section.y', 50)
351
366
  def _reduce_20(val, _values, result)
352
- result = boolean(false)
367
+ result = reference(ref: val[0])
353
368
  result
354
369
  end
355
370
  .,.,
356
371
 
357
- module_eval(<<'.,.,', 'code_section.y', 51)
372
+ module_eval(<<'.,.,', 'code_section.y', 52)
358
373
  def _reduce_21(val, _values, result)
359
- result = cell_reference(ref: val[0])
374
+ result = function_call(val[1].to_sym, [val[0], val[2]], infix: true)
360
375
  result
361
376
  end
362
377
  .,.,
363
378
 
364
- module_eval(<<'.,.,', 'code_section.y', 53)
379
+ module_eval(<<'.,.,', 'code_section.y', 54)
365
380
  def _reduce_22(val, _values, result)
366
- result = function_call(val[1].to_sym, [val[0], val[2]], infix: true)
381
+ result = function_call(val[0].to_sym, val[2])
367
382
  result
368
383
  end
369
384
  .,.,
370
385
 
371
386
  module_eval(<<'.,.,', 'code_section.y', 55)
372
387
  def _reduce_23(val, _values, result)
373
- result = function_call(val[0].to_sym, val[2])
388
+ result = function_call(val[0].to_sym, [])
374
389
  result
375
390
  end
376
391
  .,.,
377
392
 
378
- module_eval(<<'.,.,', 'code_section.y', 56)
393
+ module_eval(<<'.,.,', 'code_section.y', 57)
379
394
  def _reduce_24(val, _values, result)
380
- result = function_call(val[0].to_sym, [])
395
+ result = val[0] << val[2]
381
396
  result
382
397
  end
383
398
  .,.,
384
399
 
385
400
  module_eval(<<'.,.,', 'code_section.y', 58)
386
401
  def _reduce_25(val, _values, result)
387
- result = val[0] << val[2]
388
- result
389
- end
390
- .,.,
391
-
392
- module_eval(<<'.,.,', 'code_section.y', 59)
393
- def _reduce_26(val, _values, result)
394
402
  result = [val[0]]
395
403
  result
396
404
  end
@@ -7,16 +7,18 @@
7
7
  require 'racc/parser.rb'
8
8
 
9
9
 
10
- require_relative '../lexer'
10
+ require_relative '../lexer/racc_lexer'
11
11
 
12
12
  module CSVPlusPlus
13
13
  module Parser
14
14
  class Modifier < Racc::Parser
15
15
 
16
16
  module_eval(<<'...end modifier.y/module_eval...', 'modifier.y', 60)
17
- attr_reader :return_value
17
+ extend ::T::Sig
18
+ extend ::T::Generic
19
+ include ::CSVPlusPlus::Lexer::RaccLexer
18
20
 
19
- include ::CSVPlusPlus::Lexer
21
+ ReturnType = type_member {{ fixed: ::T.nilable(::String) }}
20
22
 
21
23
  # @param cell_modifier [Modifier]
22
24
  # @param row_modifier [Modifier]
@@ -30,6 +32,7 @@ module_eval(<<'...end modifier.y/module_eval...', 'modifier.y', 60)
30
32
 
31
33
  protected
32
34
 
35
+ sig { override.params(input: ::String).returns(::T::Boolean) }
33
36
  def anything_to_parse?(input)
34
37
  @modifiers_to_parse = input.scan(/!?\[\[/).count
35
38
 
@@ -41,10 +44,18 @@ module_eval(<<'...end modifier.y/module_eval...', 'modifier.y', 60)
41
44
  @modifiers_to_parse > 0
42
45
  end
43
46
 
47
+ sig { override.returns(::String) }
44
48
  def parse_subject
45
49
  'modifier'
46
50
  end
47
51
 
52
+ sig { override.returns(ReturnType) }
53
+ # The output of the parser
54
+ def return_value
55
+ @return_value
56
+ end
57
+
58
+ sig { override.returns(::CSVPlusPlus::Lexer::Tokenizer) }
48
59
  def tokenizer
49
60
  ::CSVPlusPlus::Lexer::Tokenizer.new(
50
61
  ignore: /\s+/,
@@ -58,26 +69,25 @@ module_eval(<<'...end modifier.y/module_eval...', 'modifier.y', 60)
58
69
  @modifiers_to_parse == 0
59
70
  end,
60
71
  tokens: [
61
- [/\bborder\b/, 'border'],
62
- [/\bbordercolor\b/, 'bordercolor'],
63
- [/\bborderstyle\b/, 'borderstyle'],
64
- [/\bcolor\b/, 'color'],
65
- [/\bexpand\b/, 'expand'],
66
- [/\bfontcolor\b/, 'fontcolor'],
67
- [/\bfontfamily\b/, 'fontfamily'],
68
- [/\bfontsize\b/, 'fontsize'],
69
- [/\bformat\b/, 'format'],
70
- [/\bfreeze\b/, 'freeze'],
71
- [/\bhalign\b/, 'halign'],
72
- [/\bnote\b/, 'note'],
73
- [/\bnumberformat\b/, 'numberformat'],
74
- [/\bvalidate\b/, 'validate'],
75
- [/\bvalign\b/, 'valign'],
76
- [/\bvar\b/, 'var'],
77
- [/-?[\d.]+/, :NUMBER],
78
- TOKEN_LIBRARY[:HEX_COLOR],
79
- [
80
- /
72
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /\bborder\b/, token: 'border'),
73
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /\bbordercolor\b/, token: 'bordercolor'),
74
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /\bborderstyle\b/, token: 'borderstyle'),
75
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /\bcolor\b/, token: 'color'),
76
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /\bexpand\b/, token: 'expand'),
77
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /\bfontcolor\b/, token: 'fontcolor'),
78
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /\bfontfamily\b/, token: 'fontfamily'),
79
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /\bfontsize\b/, token: 'fontsize'),
80
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /\bformat\b/, token: 'format'),
81
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /\bfreeze\b/, token: 'freeze'),
82
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /\bhalign\b/, token: 'halign'),
83
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /\bnote\b/, token: 'note'),
84
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /\bnumberformat\b/, token: 'numberformat'),
85
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /\bvalidate\b/, token: 'validate'),
86
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /\bvalign\b/, token: 'valign'),
87
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /\bvar\b/, token: 'var'),
88
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /-?[1-9][\d.]*/, token: :NUMBER),
89
+ ::CSVPlusPlus::Lexer::Token.new(
90
+ regexp: /
81
91
  (?:
82
92
  \w+\s*:\s*'([^'\\]|\\.)*') # allow for a single-quoted string which can accept any input and also allow
83
93
  # for escaping via backslash (i.e., 'ain\\'t won\\'t something' is valid)
@@ -90,12 +100,12 @@ module_eval(<<'...end modifier.y/module_eval...', 'modifier.y', 60)
90
100
  [\w,_:-] # no spaces at the end
91
101
  )
92
102
  /x,
93
- :RIGHT_SIDE,
94
- ],
95
- [/\[\[/, :START_CELL_MODIFIERS],
96
- [/!\[\[/, :START_ROW_MODIFIERS],
97
- [/\//, :MODIFIER_SEPARATOR],
98
- [/=/, :EQ],
103
+ token: :RIGHT_SIDE,
104
+ ),
105
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /\[\[/, token: :START_CELL_MODIFIERS),
106
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /!\[\[/, token: :START_ROW_MODIFIERS),
107
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /\//, token: :MODIFIER_SEPARATOR),
108
+ ::CSVPlusPlus::Lexer::Token.new(regexp: /=/, token: :EQ),
99
109
  ],
100
110
  alter_matches: {
101
111
  STRING: ->(s) { s.gsub(/^'|'$/, '') }
@@ -154,7 +164,7 @@ racc_action_pointer = [
154
164
  36, 52, 41, nil, nil, nil, 56, nil, -17, -1,
155
165
  nil, 41, nil, 49, 50, 51, 52, 53, 54, 55,
156
166
  56, 57, nil, 58, 59, 60, 61, 62, 63, 42,
157
- nil, 15, 59, 64, 61, 66, 66, 68, 65, 69,
167
+ nil, 15, 59, 60, 61, 62, 66, 64, 65, 69,
158
168
  67, 68, 69, 70, 71, 72, 73, nil, nil, nil,
159
169
  nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
160
170
  nil, nil, nil, nil ]
@@ -0,0 +1,50 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module CSVPlusPlus
5
+ module Reader
6
+ # Reads a CSV file
7
+ class CSV < ::CSVPlusPlus::Reader::Reader
8
+ extend ::T::Sig
9
+ extend ::T::Generic
10
+
11
+ CellValue = type_member { { fixed: ::String } }
12
+ public_constant :CellValue
13
+
14
+ sig { params(options: ::CSVPlusPlus::Options::FileOptions).void }
15
+ # Open a CSV outputter to the +output_filename+ specified by the +Options+
16
+ #
17
+ # @param options [Options] The supplied options.
18
+ def initialize(options)
19
+ super()
20
+
21
+ @options = options
22
+ @cell_values = ::T.let(
23
+ read_csv(@options.output_filename),
24
+ ::T::Array[::T::Array[::T.nilable(::CSVPlusPlus::Reader::CSV::CellValue)]]
25
+ )
26
+ end
27
+
28
+ sig { override.params(cell: ::CSVPlusPlus::Cell).returns(::T.nilable(::CSVPlusPlus::Reader::CSV::CellValue)) }
29
+ # Get the current value at the +cell+'s location.
30
+ #
31
+ # @param cell [Cell]
32
+ #
33
+ # @return [CellValue, nil]
34
+ def value_at(cell)
35
+ @cell_values[cell.row_index]&.[](cell.index)
36
+ end
37
+
38
+ protected
39
+
40
+ sig do
41
+ params(filename: ::Pathname).returns(::T::Array[::T::Array[::T.nilable(::CSVPlusPlus::Reader::CSV::CellValue)]])
42
+ end
43
+ def read_csv(filename)
44
+ return [[]] unless ::File.exist?(filename)
45
+
46
+ ::CSV.read(filename.to_s)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,129 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module CSVPlusPlus
5
+ module Reader
6
+ # A class that can read an existing Google Sheets spreadsheet
7
+ class GoogleSheets < ::CSVPlusPlus::Reader::Reader
8
+ extend ::T::Sig
9
+ extend ::T::Generic
10
+ include ::CSVPlusPlus::GoogleApiClient
11
+
12
+ CellValue = type_member { { fixed: ::String } }
13
+ public_constant :CellValue
14
+
15
+ sig { params(options: ::CSVPlusPlus::Options::GoogleSheetsOptions).void }
16
+ # Open a CSV outputter to the +output_filename+ specified by the +Options+
17
+ #
18
+ # @param options [Options::GoogleSheetsOptions] The supplied options.
19
+ def initialize(options)
20
+ super()
21
+
22
+ @options = options
23
+ @cell_values = ::T.let(nil, ::T.nilable(::T::Array[::T::Array[::T.nilable(::String)]]))
24
+ end
25
+
26
+ sig do
27
+ override.params(cell: ::CSVPlusPlus::Cell).returns(::T.nilable(::CSVPlusPlus::Reader::GoogleSheets::CellValue))
28
+ end
29
+ # Get the current value at the +cell+'s location.
30
+ #
31
+ # @param cell [Cell]
32
+ #
33
+ # @return [CellValue, nil]
34
+ def value_at(cell)
35
+ cell_values[cell.row_index]&.[](cell.index)
36
+ end
37
+
38
+ sig { returns(::T.nilable(::Google::Apis::SheetsV4::Sheet)) }
39
+ # @return [Google::Apis::SheetsV4::Sheet, nil]
40
+ def sheet
41
+ spreadsheet.sheets.find { |s| s.properties.title.strip == @options.sheet_name.strip }
42
+ end
43
+
44
+ sig { returns(::Google::Apis::SheetsV4::Spreadsheet) }
45
+ # @return [Google::Apis::SheetsV4::Spreadsheet]
46
+ def spreadsheet
47
+ @spreadsheet ||= ::T.let(
48
+ sheets_client.get_spreadsheet(@options.sheet_id),
49
+ ::T.nilable(::Google::Apis::SheetsV4::Spreadsheet)
50
+ )
51
+
52
+ unless @spreadsheet
53
+ raise(::CSVPlusPlus::Error::WriterError, "Unable to connect to google spreadsheet #{@options.sheet_id}")
54
+ end
55
+
56
+ @spreadsheet
57
+ end
58
+
59
+ private
60
+
61
+ sig { returns(::T::Array[::T::Array[::T.nilable(::String)]]) }
62
+ # rubocop:disable Metrics/MethodLength
63
+ def cell_values
64
+ return @cell_values if @cell_values
65
+
66
+ formatted_values = get_all_spreadsheet_values('FORMATTED_VALUE')
67
+ formula_values = get_all_spreadsheet_values('FORMULA')
68
+
69
+ @cell_values =
70
+ ::T.must(
71
+ ::T.let(
72
+ if formula_values&.values.nil? || formatted_values&.values.nil?
73
+ []
74
+ else
75
+ extract_current_values(::T.must(formatted_values), ::T.must(formula_values))
76
+ end,
77
+ ::T.nilable(::T::Array[::T::Array[::T.nilable(::String)]])
78
+ )
79
+ )
80
+ end
81
+ # rubocop:enable Metrics/MethodLength
82
+
83
+ sig { params(render_option: ::String).returns(::T.nilable(::Google::Apis::SheetsV4::ValueRange)) }
84
+ def get_all_spreadsheet_values(render_option)
85
+ sheets_client.get_spreadsheet_values(@options.sheet_id, full_range, value_render_option: render_option)
86
+ rescue ::Google::Apis::ClientError => e
87
+ return if e.status_code == 404
88
+
89
+ raise
90
+ end
91
+
92
+ sig do
93
+ params(
94
+ formatted_values: ::Google::Apis::SheetsV4::ValueRange,
95
+ formula_values: ::Google::Apis::SheetsV4::ValueRange
96
+ ).returns(::T::Array[::T::Array[::T.nilable(::String)]])
97
+ end
98
+ def extract_current_values(formatted_values, formula_values)
99
+ formatted_values.values.map.each_with_index do |row, x|
100
+ row.map.each_with_index do |_cell, y|
101
+ formula_value = formula_values.values[x][y]
102
+ if formula_value.is_a?(::String) && formula_value.start_with?('=')
103
+ formula_value
104
+ else
105
+ strip_to_nil(formatted_values.values[x][y])
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ sig { params(range: ::String).returns(::String) }
112
+ def format_range(range)
113
+ # "'#{@options.sheet_name}'!#{range}"
114
+ # XXX
115
+ range
116
+ end
117
+
118
+ sig { returns(::String) }
119
+ def full_range
120
+ format_range('A1:Z1000')
121
+ end
122
+
123
+ sig { params(str: ::String).returns(::T.nilable(::String)) }
124
+ def strip_to_nil(str)
125
+ str.strip.empty? ? nil : str
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,27 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module CSVPlusPlus
5
+ module Reader
6
+ # +Reader+s are used to complement +Writer+ instances by reading in the existing spreadsheet and providing
7
+ # a way to merge in results.
8
+ class Reader
9
+ extend ::T::Sig
10
+ extend ::T::Generic
11
+ extend ::T::Helpers
12
+
13
+ abstract!
14
+
15
+ CellValue = type_member
16
+ public_constant :CellValue
17
+
18
+ sig { abstract.params(cell: ::CSVPlusPlus::Cell).returns(::T.nilable(::CSVPlusPlus::Reader::Reader::CellValue)) }
19
+ # Get the current value at the +cell+'s location.
20
+ #
21
+ # @param cell [Cell]
22
+ #
23
+ # @return [CellValue, nil]
24
+ def value_at(cell); end
25
+ end
26
+ end
27
+ end