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.
- checksums.yaml +4 -4
- data/README.md +13 -3
- data/docs/CHANGELOG.md +18 -0
- data/lib/csv_plus_plus/a1_reference.rb +202 -0
- data/lib/csv_plus_plus/benchmarked_compiler.rb +3 -3
- data/lib/csv_plus_plus/cell.rb +1 -35
- data/lib/csv_plus_plus/cli.rb +43 -80
- data/lib/csv_plus_plus/cli_flag.rb +77 -70
- data/lib/csv_plus_plus/color.rb +1 -1
- data/lib/csv_plus_plus/compiler.rb +31 -21
- data/lib/csv_plus_plus/entities/ast_builder.rb +11 -4
- data/lib/csv_plus_plus/entities/boolean.rb +16 -9
- data/lib/csv_plus_plus/entities/builtins.rb +68 -40
- data/lib/csv_plus_plus/entities/date.rb +14 -11
- data/lib/csv_plus_plus/entities/entity.rb +11 -29
- data/lib/csv_plus_plus/entities/entity_with_arguments.rb +18 -31
- data/lib/csv_plus_plus/entities/function.rb +22 -11
- data/lib/csv_plus_plus/entities/function_call.rb +35 -11
- data/lib/csv_plus_plus/entities/has_identifier.rb +19 -0
- data/lib/csv_plus_plus/entities/number.rb +15 -10
- data/lib/csv_plus_plus/entities/reference.rb +77 -0
- data/lib/csv_plus_plus/entities/runtime_value.rb +36 -23
- data/lib/csv_plus_plus/entities/string.rb +13 -10
- data/lib/csv_plus_plus/entities.rb +2 -18
- data/lib/csv_plus_plus/error/cli_error.rb +17 -0
- data/lib/csv_plus_plus/error/compiler_error.rb +17 -0
- data/lib/csv_plus_plus/error/error.rb +18 -5
- data/lib/csv_plus_plus/error/formula_syntax_error.rb +12 -13
- data/lib/csv_plus_plus/error/modifier_syntax_error.rb +10 -36
- data/lib/csv_plus_plus/error/modifier_validation_error.rb +6 -32
- data/lib/csv_plus_plus/error/positional_error.rb +15 -0
- data/lib/csv_plus_plus/error/writer_error.rb +1 -1
- data/lib/csv_plus_plus/error.rb +4 -1
- data/lib/csv_plus_plus/error_formatter.rb +111 -0
- data/lib/csv_plus_plus/google_api_client.rb +18 -8
- data/lib/csv_plus_plus/lexer/racc_lexer.rb +144 -0
- data/lib/csv_plus_plus/lexer/tokenizer.rb +53 -17
- data/lib/csv_plus_plus/lexer.rb +40 -1
- data/lib/csv_plus_plus/modifier/data_validation.rb +1 -1
- data/lib/csv_plus_plus/modifier/expand.rb +17 -0
- data/lib/csv_plus_plus/modifier.rb +6 -1
- data/lib/csv_plus_plus/options/file_options.rb +49 -0
- data/lib/csv_plus_plus/options/google_sheets_options.rb +42 -0
- data/lib/csv_plus_plus/options/options.rb +102 -0
- data/lib/csv_plus_plus/options.rb +22 -110
- data/lib/csv_plus_plus/parser/cell_value.tab.rb +65 -66
- data/lib/csv_plus_plus/parser/code_section.tab.rb +92 -84
- data/lib/csv_plus_plus/parser/modifier.tab.rb +40 -30
- data/lib/csv_plus_plus/reader/csv.rb +50 -0
- data/lib/csv_plus_plus/reader/google_sheets.rb +129 -0
- data/lib/csv_plus_plus/reader/reader.rb +27 -0
- data/lib/csv_plus_plus/reader/rubyxl.rb +37 -0
- data/lib/csv_plus_plus/reader.rb +14 -0
- data/lib/csv_plus_plus/runtime/graph.rb +6 -6
- data/lib/csv_plus_plus/runtime/{position_tracker.rb → position.rb} +16 -5
- data/lib/csv_plus_plus/runtime/references.rb +32 -27
- data/lib/csv_plus_plus/runtime/runtime.rb +73 -67
- data/lib/csv_plus_plus/runtime/scope.rb +280 -0
- data/lib/csv_plus_plus/runtime.rb +9 -9
- data/lib/csv_plus_plus/source_code.rb +14 -9
- data/lib/csv_plus_plus/template.rb +17 -12
- data/lib/csv_plus_plus/version.rb +1 -1
- data/lib/csv_plus_plus/writer/csv.rb +32 -5
- data/lib/csv_plus_plus/writer/excel.rb +19 -6
- data/lib/csv_plus_plus/writer/file_backer_upper.rb +27 -14
- data/lib/csv_plus_plus/writer/google_sheets.rb +23 -129
- data/lib/csv_plus_plus/writer/{google_sheet_builder.rb → google_sheets_builder.rb} +39 -55
- data/lib/csv_plus_plus/writer/merger.rb +56 -0
- data/lib/csv_plus_plus/writer/open_document.rb +16 -2
- data/lib/csv_plus_plus/writer/rubyxl_builder.rb +68 -43
- data/lib/csv_plus_plus/writer/writer.rb +42 -0
- data/lib/csv_plus_plus/writer.rb +58 -19
- data/lib/csv_plus_plus.rb +26 -14
- metadata +43 -18
- data/lib/csv_plus_plus/entities/cell_reference.rb +0 -231
- data/lib/csv_plus_plus/entities/variable.rb +0 -37
- data/lib/csv_plus_plus/error/syntax_error.rb +0 -71
- data/lib/csv_plus_plus/google_options.rb +0 -32
- data/lib/csv_plus_plus/lexer/lexer.rb +0 -89
- data/lib/csv_plus_plus/runtime/can_define_references.rb +0 -87
- data/lib/csv_plus_plus/runtime/can_resolve_references.rb +0 -209
- 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',
|
|
17
|
-
|
|
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
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
TOKEN_LIBRARY[:TRUE],
|
|
47
|
-
TOKEN_LIBRARY[:FALSE],
|
|
48
|
-
TOKEN_LIBRARY[:NUMBER],
|
|
49
|
-
TOKEN_LIBRARY[:STRING],
|
|
50
|
-
TOKEN_LIBRARY[:INFIX_OP],
|
|
51
|
-
TOKEN_LIBRARY[:
|
|
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
|
-
|
|
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
|
-
@
|
|
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,
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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,
|
|
86
|
-
|
|
87
|
-
13, 13, 13,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
96
|
-
nil, nil,
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
-
|
|
103
|
-
-1, -3, -
|
|
104
|
-
-
|
|
105
|
-
-
|
|
106
|
-
-
|
|
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,
|
|
110
|
-
nil, nil, nil, nil, nil, nil, nil, nil, 37,
|
|
111
|
-
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,
|
|
117
|
-
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, -
|
|
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
|
-
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
3,
|
|
154
|
-
|
|
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 =
|
|
172
|
+
racc_reduce_n = 26
|
|
158
173
|
|
|
159
|
-
racc_shift_n =
|
|
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
|
-
:
|
|
184
|
-
:
|
|
185
|
-
:
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
367
|
+
result = reference(ref: val[0])
|
|
353
368
|
result
|
|
354
369
|
end
|
|
355
370
|
.,.,
|
|
356
371
|
|
|
357
|
-
module_eval(<<'.,.,', 'code_section.y',
|
|
372
|
+
module_eval(<<'.,.,', 'code_section.y', 52)
|
|
358
373
|
def _reduce_21(val, _values, result)
|
|
359
|
-
result =
|
|
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',
|
|
379
|
+
module_eval(<<'.,.,', 'code_section.y', 54)
|
|
365
380
|
def _reduce_22(val, _values, result)
|
|
366
|
-
result = function_call(val[
|
|
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,
|
|
388
|
+
result = function_call(val[0].to_sym, [])
|
|
374
389
|
result
|
|
375
390
|
end
|
|
376
391
|
.,.,
|
|
377
392
|
|
|
378
|
-
module_eval(<<'.,.,', 'code_section.y',
|
|
393
|
+
module_eval(<<'.,.,', 'code_section.y', 57)
|
|
379
394
|
def _reduce_24(val, _values, result)
|
|
380
|
-
result =
|
|
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
|
-
|
|
17
|
+
extend ::T::Sig
|
|
18
|
+
extend ::T::Generic
|
|
19
|
+
include ::CSVPlusPlus::Lexer::RaccLexer
|
|
18
20
|
|
|
19
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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,
|
|
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
|