csv_plus_plus 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -3
  3. data/docs/CHANGELOG.md +16 -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 +71 -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 +97 -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 +31 -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 +37 -12
  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