csv_plus_plus 0.1.2 → 0.1.3
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 +1 -2
- data/{CHANGELOG.md → docs/CHANGELOG.md} +9 -0
- data/lib/csv_plus_plus/benchmarked_compiler.rb +70 -20
- data/lib/csv_plus_plus/cell.rb +46 -24
- data/lib/csv_plus_plus/cli.rb +23 -13
- data/lib/csv_plus_plus/cli_flag.rb +1 -2
- data/lib/csv_plus_plus/color.rb +32 -7
- data/lib/csv_plus_plus/compiler.rb +82 -60
- data/lib/csv_plus_plus/entities/ast_builder.rb +27 -43
- data/lib/csv_plus_plus/entities/boolean.rb +18 -9
- data/lib/csv_plus_plus/entities/builtins.rb +23 -9
- data/lib/csv_plus_plus/entities/cell_reference.rb +200 -29
- data/lib/csv_plus_plus/entities/date.rb +38 -5
- data/lib/csv_plus_plus/entities/entity.rb +27 -61
- data/lib/csv_plus_plus/entities/entity_with_arguments.rb +57 -0
- data/lib/csv_plus_plus/entities/function.rb +23 -11
- data/lib/csv_plus_plus/entities/function_call.rb +24 -9
- data/lib/csv_plus_plus/entities/number.rb +24 -10
- data/lib/csv_plus_plus/entities/runtime_value.rb +22 -5
- data/lib/csv_plus_plus/entities/string.rb +19 -6
- data/lib/csv_plus_plus/entities/variable.rb +16 -4
- data/lib/csv_plus_plus/entities.rb +20 -13
- data/lib/csv_plus_plus/error/error.rb +11 -1
- data/lib/csv_plus_plus/error/formula_syntax_error.rb +1 -0
- data/lib/csv_plus_plus/error/modifier_syntax_error.rb +53 -5
- data/lib/csv_plus_plus/error/modifier_validation_error.rb +34 -14
- data/lib/csv_plus_plus/error/syntax_error.rb +22 -9
- data/lib/csv_plus_plus/error/writer_error.rb +8 -0
- data/lib/csv_plus_plus/error.rb +1 -0
- data/lib/csv_plus_plus/google_api_client.rb +7 -2
- data/lib/csv_plus_plus/google_options.rb +23 -18
- data/lib/csv_plus_plus/lexer/lexer.rb +8 -4
- data/lib/csv_plus_plus/lexer/tokenizer.rb +6 -1
- data/lib/csv_plus_plus/lexer.rb +24 -0
- data/lib/csv_plus_plus/modifier/conditional_formatting.rb +1 -0
- data/lib/csv_plus_plus/modifier/data_validation.rb +138 -0
- data/lib/csv_plus_plus/modifier/expand.rb +61 -0
- data/lib/csv_plus_plus/modifier/google_sheet_modifier.rb +133 -0
- data/lib/csv_plus_plus/modifier/modifier.rb +222 -0
- data/lib/csv_plus_plus/modifier/modifier_validator.rb +243 -0
- data/lib/csv_plus_plus/modifier/rubyxl_modifier.rb +84 -0
- data/lib/csv_plus_plus/modifier.rb +82 -158
- data/lib/csv_plus_plus/options.rb +64 -19
- data/lib/csv_plus_plus/parser/cell_value.tab.rb +5 -5
- data/lib/csv_plus_plus/parser/code_section.tab.rb +8 -13
- data/lib/csv_plus_plus/parser/modifier.tab.rb +17 -23
- data/lib/csv_plus_plus/row.rb +53 -12
- data/lib/csv_plus_plus/runtime/can_define_references.rb +87 -0
- data/lib/csv_plus_plus/runtime/can_resolve_references.rb +209 -0
- data/lib/csv_plus_plus/runtime/graph.rb +68 -0
- data/lib/csv_plus_plus/runtime/position_tracker.rb +231 -0
- data/lib/csv_plus_plus/runtime/references.rb +110 -0
- data/lib/csv_plus_plus/runtime/runtime.rb +126 -0
- data/lib/csv_plus_plus/runtime.rb +34 -191
- data/lib/csv_plus_plus/source_code.rb +66 -0
- data/lib/csv_plus_plus/template.rb +62 -35
- data/lib/csv_plus_plus/version.rb +2 -1
- data/lib/csv_plus_plus/writer/base_writer.rb +30 -5
- data/lib/csv_plus_plus/writer/csv.rb +11 -9
- data/lib/csv_plus_plus/writer/excel.rb +9 -2
- data/lib/csv_plus_plus/writer/file_backer_upper.rb +1 -0
- data/lib/csv_plus_plus/writer/google_sheet_builder.rb +71 -23
- data/lib/csv_plus_plus/writer/google_sheets.rb +79 -29
- data/lib/csv_plus_plus/writer/open_document.rb +6 -1
- data/lib/csv_plus_plus/writer/rubyxl_builder.rb +103 -30
- data/lib/csv_plus_plus/writer.rb +39 -9
- data/lib/csv_plus_plus.rb +29 -12
- metadata +18 -14
- data/lib/csv_plus_plus/can_define_references.rb +0 -88
- data/lib/csv_plus_plus/can_resolve_references.rb +0 -8
- data/lib/csv_plus_plus/data_validation.rb +0 -138
- data/lib/csv_plus_plus/expand.rb +0 -20
- data/lib/csv_plus_plus/graph.rb +0 -62
- data/lib/csv_plus_plus/references.rb +0 -68
- data/lib/csv_plus_plus/scope.rb +0 -196
- data/lib/csv_plus_plus/validated_modifier.rb +0 -164
- data/lib/csv_plus_plus/writer/google_sheet_modifier.rb +0 -77
- data/lib/csv_plus_plus/writer/rubyxl_modifier.rb +0 -59
@@ -1,8 +1,6 @@
|
|
1
|
+
# typed: strict
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require_relative './cli_flag'
|
4
|
-
require_relative './google_options'
|
5
|
-
|
6
4
|
module CSVPlusPlus
|
7
5
|
# The options a user can supply (via CLI flags)
|
8
6
|
#
|
@@ -15,28 +13,80 @@ module CSVPlusPlus
|
|
15
13
|
# @attr verbose [boolean] Include extra verbose output?
|
16
14
|
# @attr_reader google [GoogleOptions] Options that are specific to the Google Sheets writer
|
17
15
|
class Options
|
18
|
-
|
16
|
+
extend ::T::Sig
|
17
|
+
|
18
|
+
# The supported output formats. We use this to dispatch flow in several places
|
19
|
+
class OutputFormat < ::T::Enum
|
20
|
+
enums do
|
21
|
+
CSV = new
|
22
|
+
Excel = new
|
23
|
+
GoogleSheets = new
|
24
|
+
OpenDocument = new
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
sig { returns(::T::Boolean) }
|
29
|
+
attr_accessor :backup
|
30
|
+
|
31
|
+
sig { returns(::T::Boolean) }
|
32
|
+
attr_accessor :create_if_not_exists
|
33
|
+
|
34
|
+
sig { returns(::T::Hash[::Symbol, ::String]) }
|
35
|
+
attr_accessor :key_values
|
36
|
+
|
37
|
+
sig { returns(::T::Array[::Integer]) }
|
38
|
+
attr_accessor :offset
|
39
|
+
|
40
|
+
sig { returns(::T.nilable(::String)) }
|
41
|
+
attr_accessor :output_filename
|
42
|
+
|
43
|
+
sig { returns(::T.nilable(::String)) }
|
44
|
+
attr_accessor :sheet_name
|
45
|
+
|
46
|
+
sig { returns(::T::Boolean) }
|
47
|
+
attr_accessor :verbose
|
48
|
+
|
49
|
+
sig { returns(::T.nilable(::CSVPlusPlus::GoogleOptions)) }
|
19
50
|
attr_reader :google
|
20
51
|
|
21
|
-
|
52
|
+
sig { void }
|
53
|
+
# Initialize a default +Options+ object
|
22
54
|
def initialize
|
23
|
-
@offset = [0, 0]
|
24
|
-
@create_if_not_exists = false
|
25
|
-
@key_values = {}
|
26
|
-
@verbose = false
|
27
|
-
@backup = false
|
55
|
+
@offset = ::T.let([0, 0], ::T::Array[::Integer])
|
56
|
+
@create_if_not_exists = ::T.let(false, ::T::Boolean)
|
57
|
+
@key_values = ::T.let({}, ::T::Hash[::Symbol, ::String])
|
58
|
+
@verbose = ::T.let(false, ::T::Boolean)
|
59
|
+
@backup = ::T.let(false, ::T::Boolean)
|
60
|
+
@google = ::T.let(nil, ::T.nilable(::CSVPlusPlus::GoogleOptions))
|
28
61
|
end
|
29
62
|
|
63
|
+
sig { params(sheet_id: ::String).returns(::CSVPlusPlus::GoogleOptions) }
|
30
64
|
# Set the Google Sheet ID
|
31
65
|
#
|
32
|
-
# @param sheet_id [String] The identifier used by Google's API to reference the sheet. You can find it in the URL
|
66
|
+
# @param sheet_id [::String] The identifier used by Google's API to reference the sheet. You can find it in the URL
|
33
67
|
# for the sheet
|
34
68
|
#
|
35
|
-
# @return [String]
|
69
|
+
# @return [::String]
|
36
70
|
def google_sheet_id=(sheet_id)
|
37
71
|
@google = ::CSVPlusPlus::GoogleOptions.new(sheet_id)
|
38
72
|
end
|
39
73
|
|
74
|
+
sig { returns(::CSVPlusPlus::Options::OutputFormat) }
|
75
|
+
# Given the options, figure out which type of +OutputFormat+ we'll be writing to
|
76
|
+
#
|
77
|
+
# @return [Options::OutputFormat]
|
78
|
+
def output_format
|
79
|
+
return ::CSVPlusPlus::Options::OutputFormat::GoogleSheets if @google
|
80
|
+
|
81
|
+
case @output_filename
|
82
|
+
when /\.csv$/ then ::CSVPlusPlus::Options::OutputFormat::CSV
|
83
|
+
when /\.ods$/ then ::CSVPlusPlus::Options::OutputFormat::OpenDocument
|
84
|
+
when /\.xl(sx|sm|tx|tm)$/ then ::CSVPlusPlus::Options::OutputFormat::Excel
|
85
|
+
else raise(::CSVPlusPlus::Error::Error, "Unsupported file extension: #{@output_filename}")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
sig { returns(::T.nilable(::String)) }
|
40
90
|
# Returns an error string or nil if there are no validation problems
|
41
91
|
#
|
42
92
|
# @return [String, nil]
|
@@ -46,6 +96,7 @@ module CSVPlusPlus
|
|
46
96
|
'You must supply either a Google Sheet ID or an output file'
|
47
97
|
end
|
48
98
|
|
99
|
+
sig { returns(::String) }
|
49
100
|
# Return a string with a verbose description of what we're doing with the options
|
50
101
|
#
|
51
102
|
# @return [String]
|
@@ -55,7 +106,6 @@ module CSVPlusPlus
|
|
55
106
|
|
56
107
|
# csv++ Command Options
|
57
108
|
|
58
|
-
> Input filename | #{@filename}
|
59
109
|
> Sheet name | #{@sheet_name}
|
60
110
|
> Create sheet if it does not exist? | #{@create_if_not_exists}
|
61
111
|
> Spreadsheet row-offset | #{@offset[0]}
|
@@ -73,14 +123,9 @@ module CSVPlusPlus
|
|
73
123
|
SUMMARY
|
74
124
|
end
|
75
125
|
|
76
|
-
# @return [String]
|
77
|
-
def to_s
|
78
|
-
"Options(create_if_not_exists: #{@create_if_not_exists}, google: #{@google}, key_values: #{@key_values}, " \
|
79
|
-
"offset: #{@offset}, sheet_name: #{@sheet_name}, verbose: #{@verbose})"
|
80
|
-
end
|
81
|
-
|
82
126
|
private
|
83
127
|
|
128
|
+
sig { returns(::String) }
|
84
129
|
def summary_divider
|
85
130
|
'========================================================================='
|
86
131
|
end
|
@@ -221,7 +221,7 @@ module_eval(<<'.,.,', 'cell_value.y', 25)
|
|
221
221
|
|
222
222
|
module_eval(<<'.,.,', 'cell_value.y', 26)
|
223
223
|
def _reduce_5(val, _values, result)
|
224
|
-
result = variable(val[1])
|
224
|
+
result = variable(val[1].to_sym)
|
225
225
|
result
|
226
226
|
end
|
227
227
|
.,.,
|
@@ -256,21 +256,21 @@ module_eval(<<'.,.,', 'cell_value.y', 30)
|
|
256
256
|
|
257
257
|
module_eval(<<'.,.,', 'cell_value.y', 31)
|
258
258
|
def _reduce_10(val, _values, result)
|
259
|
-
result = cell_reference(val[0])
|
259
|
+
result = cell_reference(ref: val[0])
|
260
260
|
result
|
261
261
|
end
|
262
262
|
.,.,
|
263
263
|
|
264
264
|
module_eval(<<'.,.,', 'cell_value.y', 33)
|
265
265
|
def _reduce_11(val, _values, result)
|
266
|
-
result = function_call(val[0], val[2])
|
266
|
+
result = function_call(val[0].to_sym, val[2])
|
267
267
|
result
|
268
268
|
end
|
269
269
|
.,.,
|
270
270
|
|
271
271
|
module_eval(<<'.,.,', 'cell_value.y', 34)
|
272
272
|
def _reduce_12(val, _values, result)
|
273
|
-
result = function_call(val[0], [])
|
273
|
+
result = function_call(val[0].to_sym, [])
|
274
274
|
result
|
275
275
|
end
|
276
276
|
.,.,
|
@@ -291,7 +291,7 @@ module_eval(<<'.,.,', 'cell_value.y', 37)
|
|
291
291
|
|
292
292
|
module_eval(<<'.,.,', 'cell_value.y', 39)
|
293
293
|
def _reduce_15(val, _values, result)
|
294
|
-
result = function_call(val[1], [val[0], val[2]], infix: true)
|
294
|
+
result = function_call(val[1].to_sym, [val[0], val[2]], infix: true)
|
295
295
|
result
|
296
296
|
end
|
297
297
|
.,.,
|
@@ -17,11 +17,6 @@ module_eval(<<'...end code_section.y/module_eval...', 'code_section.y', 69)
|
|
17
17
|
include ::CSVPlusPlus::Lexer
|
18
18
|
include ::CSVPlusPlus::Entities::ASTBuilder
|
19
19
|
|
20
|
-
def initialize(scope)
|
21
|
-
super()
|
22
|
-
@scope = scope
|
23
|
-
end
|
24
|
-
|
25
20
|
protected
|
26
21
|
|
27
22
|
def anything_to_parse?(input)
|
@@ -66,12 +61,12 @@ module_eval(<<'...end code_section.y/module_eval...', 'code_section.y', 69)
|
|
66
61
|
private
|
67
62
|
|
68
63
|
def def_function(id, arguments, body)
|
69
|
-
fn_def = function(id, arguments, body)
|
70
|
-
@
|
64
|
+
fn_def = function(id.to_sym, arguments, body)
|
65
|
+
@runtime.def_function(fn_def.id, fn_def)
|
71
66
|
end
|
72
67
|
|
73
68
|
def def_variable(id, ast)
|
74
|
-
@
|
69
|
+
@runtime.def_variable(id.to_sym, ast)
|
75
70
|
end
|
76
71
|
...end code_section.y/module_eval...
|
77
72
|
##### State transition tables begin ###
|
@@ -326,7 +321,7 @@ module_eval(<<'.,.,', 'code_section.y', 45)
|
|
326
321
|
|
327
322
|
module_eval(<<'.,.,', 'code_section.y', 46)
|
328
323
|
def _reduce_16(val, _values, result)
|
329
|
-
result = variable(val[1])
|
324
|
+
result = variable(val[1].to_sym)
|
330
325
|
result
|
331
326
|
end
|
332
327
|
.,.,
|
@@ -361,28 +356,28 @@ module_eval(<<'.,.,', 'code_section.y', 50)
|
|
361
356
|
|
362
357
|
module_eval(<<'.,.,', 'code_section.y', 51)
|
363
358
|
def _reduce_21(val, _values, result)
|
364
|
-
result = cell_reference(val[0])
|
359
|
+
result = cell_reference(ref: val[0])
|
365
360
|
result
|
366
361
|
end
|
367
362
|
.,.,
|
368
363
|
|
369
364
|
module_eval(<<'.,.,', 'code_section.y', 53)
|
370
365
|
def _reduce_22(val, _values, result)
|
371
|
-
result = function_call(val[1], [val[0], val[2]], infix: true)
|
366
|
+
result = function_call(val[1].to_sym, [val[0], val[2]], infix: true)
|
372
367
|
result
|
373
368
|
end
|
374
369
|
.,.,
|
375
370
|
|
376
371
|
module_eval(<<'.,.,', 'code_section.y', 55)
|
377
372
|
def _reduce_23(val, _values, result)
|
378
|
-
result = function_call(val[0], val[2])
|
373
|
+
result = function_call(val[0].to_sym, val[2])
|
379
374
|
result
|
380
375
|
end
|
381
376
|
.,.,
|
382
377
|
|
383
378
|
module_eval(<<'.,.,', 'code_section.y', 56)
|
384
379
|
def _reduce_24(val, _values, result)
|
385
|
-
result = function_call(val[0], [])
|
380
|
+
result = function_call(val[0].to_sym, [])
|
386
381
|
result
|
387
382
|
end
|
388
383
|
.,.,
|
@@ -7,28 +7,25 @@
|
|
7
7
|
require 'racc/parser.rb'
|
8
8
|
|
9
9
|
|
10
|
-
require_relative '../expand'
|
11
10
|
require_relative '../lexer'
|
12
11
|
|
13
12
|
module CSVPlusPlus
|
14
13
|
module Parser
|
15
14
|
class Modifier < Racc::Parser
|
16
15
|
|
17
|
-
module_eval(<<'...end modifier.y/module_eval...', 'modifier.y',
|
16
|
+
module_eval(<<'...end modifier.y/module_eval...', 'modifier.y', 60)
|
18
17
|
attr_reader :return_value
|
19
18
|
|
20
19
|
include ::CSVPlusPlus::Lexer
|
21
20
|
|
22
21
|
# @param cell_modifier [Modifier]
|
23
22
|
# @param row_modifier [Modifier]
|
24
|
-
|
25
|
-
def initialize(cell_modifier:, row_modifier:, scope:)
|
23
|
+
def initialize(cell_modifier:, row_modifier:)
|
26
24
|
super()
|
27
25
|
|
28
26
|
@parsing_row = false
|
29
|
-
@cell_modifier = cell_modifier
|
30
|
-
@row_modifier = row_modifier
|
31
|
-
@scope = scope
|
27
|
+
@cell_modifier = ::CSVPlusPlus::Modifier::ModifierValidator.new(cell_modifier)
|
28
|
+
@row_modifier = ::CSVPlusPlus::Modifier::ModifierValidator.new(row_modifier)
|
32
29
|
end
|
33
30
|
|
34
31
|
protected
|
@@ -82,14 +79,16 @@ module_eval(<<'...end modifier.y/module_eval...', 'modifier.y', 61)
|
|
82
79
|
[
|
83
80
|
/
|
84
81
|
(?:
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
)
|
89
|
-
|
|
82
|
+
\w+\s*:\s*'([^'\\]|\\.)*') # allow for a single-quoted string which can accept any input and also allow
|
83
|
+
# for escaping via backslash (i.e., 'ain\\'t won\\'t something' is valid)
|
84
|
+
| # - or -
|
85
|
+
(?:'([^'\\]|\\.)*') # allow for a single-quoted string which can accept any input and also allow
|
86
|
+
|
|
90
87
|
(?:
|
91
|
-
|
92
|
-
|
88
|
+
[\w,_:-] # something that accepts most basic input if it doesn't need to be quoted
|
89
|
+
[\w\s,_:-]+ # same thing but allow spaces in the middle
|
90
|
+
[\w,_:-] # no spaces at the end
|
91
|
+
)
|
93
92
|
/x,
|
94
93
|
:RIGHT_SIDE,
|
95
94
|
],
|
@@ -107,7 +106,7 @@ module_eval(<<'...end modifier.y/module_eval...', 'modifier.y', 61)
|
|
107
106
|
private
|
108
107
|
|
109
108
|
def assign_defaults!
|
110
|
-
@cell_modifier.take_defaults_from!(@row_modifier)
|
109
|
+
@cell_modifier.modifier.take_defaults_from!(@row_modifier.modifier)
|
111
110
|
end
|
112
111
|
|
113
112
|
def parsing_row!
|
@@ -123,11 +122,6 @@ module_eval(<<'...end modifier.y/module_eval...', 'modifier.y', 61)
|
|
123
122
|
assign_defaults!
|
124
123
|
end
|
125
124
|
|
126
|
-
def define_var(var_id)
|
127
|
-
@scope.bind_variable_to_cell(var_id)
|
128
|
-
modifier.var = var_id.to_sym
|
129
|
-
end
|
130
|
-
|
131
125
|
def modifier
|
132
126
|
@parsing_row ? @row_modifier : @cell_modifier
|
133
127
|
end
|
@@ -393,7 +387,7 @@ module_eval(<<'.,.,', 'modifier.y', 39)
|
|
393
387
|
|
394
388
|
module_eval(<<'.,.,', 'modifier.y', 40)
|
395
389
|
def _reduce_15(val, _values, result)
|
396
|
-
modifier.
|
390
|
+
modifier.infinite_expand!
|
397
391
|
result
|
398
392
|
end
|
399
393
|
.,.,
|
@@ -456,7 +450,7 @@ module_eval(<<'.,.,', 'modifier.y', 48)
|
|
456
450
|
|
457
451
|
module_eval(<<'.,.,', 'modifier.y', 49)
|
458
452
|
def _reduce_24(val, _values, result)
|
459
|
-
modifier.
|
453
|
+
modifier.validate = val[2]
|
460
454
|
result
|
461
455
|
end
|
462
456
|
.,.,
|
@@ -470,7 +464,7 @@ module_eval(<<'.,.,', 'modifier.y', 50)
|
|
470
464
|
|
471
465
|
module_eval(<<'.,.,', 'modifier.y', 51)
|
472
466
|
def _reduce_26(val, _values, result)
|
473
|
-
|
467
|
+
modifier.var = val[2]
|
474
468
|
result
|
475
469
|
end
|
476
470
|
.,.,
|
data/lib/csv_plus_plus/row.rb
CHANGED
@@ -1,23 +1,37 @@
|
|
1
|
+
# typed: strict
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module CSVPlusPlus
|
4
|
-
# A row of a template
|
5
|
+
# A row of a template. A row contains an +Array+ of +Cell+s and possibly a row-level +Modifier+.
|
5
6
|
#
|
6
|
-
# @attr_reader cells [Array<Cell>]
|
7
|
-
# @attr_reader index [Integer] The index of this row
|
7
|
+
# @attr_reader cells [Array<Cell>] The cells contained by this row.
|
8
|
+
# @attr_reader index [Integer] The index of this row. Starts at 0.
|
8
9
|
# @attr_reader modifier [Modifier] The modifier to apply to all cells in this row
|
9
10
|
class Row
|
10
|
-
|
11
|
+
extend ::T::Sig
|
11
12
|
|
12
|
-
|
13
|
+
sig { returns(::T::Array[::CSVPlusPlus::Cell]) }
|
14
|
+
attr_reader :cells
|
15
|
+
|
16
|
+
sig { returns(::Integer) }
|
17
|
+
attr_reader :index
|
18
|
+
|
19
|
+
sig { returns(::CSVPlusPlus::Modifier::Modifier) }
|
20
|
+
attr_reader :modifier
|
21
|
+
|
22
|
+
sig do
|
23
|
+
params(cells: ::T::Array[::CSVPlusPlus::Cell], index: ::Integer, modifier: ::CSVPlusPlus::Modifier::Modifier).void
|
24
|
+
end
|
13
25
|
# @param cells [Array<Cell>] The cells belonging to this row
|
26
|
+
# @param index [Integer] The index of this row (starts at 0)
|
14
27
|
# @param modifier [Modifier] The modifier to apply to all cells in this row
|
15
|
-
def initialize(index
|
28
|
+
def initialize(cells:, index:, modifier:)
|
16
29
|
@cells = cells
|
17
30
|
@modifier = modifier
|
18
31
|
@index = index
|
19
32
|
end
|
20
33
|
|
34
|
+
sig { params(index: ::Integer).void }
|
21
35
|
# Set the row's +index+ and update the +row_index+ of all affected cells
|
22
36
|
#
|
23
37
|
# @param index [Integer] The index of this row (starts at 0)
|
@@ -26,25 +40,52 @@ module CSVPlusPlus
|
|
26
40
|
@cells.each { |cell| cell.row_index = index }
|
27
41
|
end
|
28
42
|
|
43
|
+
sig { returns(::Integer) }
|
29
44
|
# How much this row will expand itself, if at all (0)
|
30
45
|
#
|
31
46
|
# @return [Integer]
|
32
47
|
def expand_amount
|
33
|
-
return 0
|
48
|
+
return 0 if @modifier.expand.nil?
|
34
49
|
|
35
|
-
@modifier.expand.repetitions || (1000 - @index)
|
50
|
+
::T.must(@modifier.expand).repetitions || (1000 - @index)
|
36
51
|
end
|
37
52
|
|
38
|
-
|
39
|
-
|
40
|
-
|
53
|
+
sig { params(starts_at: ::Integer, into: ::T::Array[::CSVPlusPlus::Row]).returns(::T::Array[::CSVPlusPlus::Row]) }
|
54
|
+
# Starting at +starts_at+, do a deep copy of this row into the +Array+ referenced by +into+.
|
55
|
+
#
|
56
|
+
# @param starts_at [Integer] The +row_index+ where this row was expanded.
|
57
|
+
# @param into [Array<Row>] An array where the expanded rows will be accumulated.
|
58
|
+
#
|
59
|
+
# @return [Array<Row>] The rows expanded
|
60
|
+
def expand_rows(starts_at:, into: [])
|
61
|
+
return into if @modifier.expand.nil?
|
62
|
+
|
63
|
+
::T.must(@modifier.expand).starts_at = starts_at
|
64
|
+
|
65
|
+
starts_at.upto(expand_amount + starts_at - 1) do |row_index|
|
66
|
+
into << deep_clone.tap { |c| c.index = row_index }
|
67
|
+
end
|
68
|
+
into
|
41
69
|
end
|
42
70
|
|
71
|
+
sig { returns(::T::Boolean) }
|
72
|
+
# Does the row have an ![[expand]] modifier but is yet to be expanded?
|
73
|
+
#
|
74
|
+
# @return [T::Boolean]
|
75
|
+
def unexpanded?
|
76
|
+
return false if @modifier.expand.nil?
|
77
|
+
|
78
|
+
!::T.must(@modifier.expand).expanded?
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
sig { returns(::CSVPlusPlus::Row) }
|
43
84
|
# Return a deep copy of this row
|
44
85
|
#
|
45
86
|
# @return [Row]
|
46
87
|
def deep_clone
|
47
|
-
::Marshal.load(::Marshal.dump(self))
|
88
|
+
::T.cast(::Marshal.load(::Marshal.dump(self)), ::CSVPlusPlus::Row)
|
48
89
|
end
|
49
90
|
end
|
50
91
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module CSVPlusPlus
|
5
|
+
module Runtime
|
6
|
+
# Methods for classes that need to manage +@variables+ and +@functions+
|
7
|
+
module CanDefineReferences
|
8
|
+
# Define a (or re-define an existing) variable
|
9
|
+
#
|
10
|
+
# @param id [String, Symbol] The identifier for the variable
|
11
|
+
# @param entity [Entity] The value (entity) the variable holds
|
12
|
+
#
|
13
|
+
# @return [Entity] The value of the variable (+entity+)
|
14
|
+
def def_variable(id, entity)
|
15
|
+
@variables[id.to_sym] = entity
|
16
|
+
end
|
17
|
+
|
18
|
+
# Define (or re-define existing) variables
|
19
|
+
#
|
20
|
+
# @param vars [Hash<Symbol, Variable>] Variables to define
|
21
|
+
def def_variables(vars)
|
22
|
+
vars.each { |id, entity| def_variable(id, entity) }
|
23
|
+
end
|
24
|
+
|
25
|
+
# Define a (or re-define an existing) function
|
26
|
+
#
|
27
|
+
# @param id [String, Symbol] The identifier for the function
|
28
|
+
# @param entity [Entities::Function] The defined function
|
29
|
+
#
|
30
|
+
# @return [Entities::Function] The defined function
|
31
|
+
def def_function(id, entity)
|
32
|
+
@functions[id.to_sym] = entity
|
33
|
+
end
|
34
|
+
|
35
|
+
# Is the variable defined?
|
36
|
+
#
|
37
|
+
# @param var_id [Symbol, String] The identifier of the variable
|
38
|
+
#
|
39
|
+
# @return [boolean]
|
40
|
+
def defined_variable?(var_id)
|
41
|
+
@variables.key?(var_id.to_sym)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Is the function defined?
|
45
|
+
#
|
46
|
+
# @param fn_id [Symbol, String] The identifier of the function
|
47
|
+
#
|
48
|
+
# @return [boolean]
|
49
|
+
def defined_function?(fn_id)
|
50
|
+
@functions.key?(fn_id.to_sym)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Provide a summary of the functions and variables compiled (to show in verbose mode)
|
54
|
+
#
|
55
|
+
# @return [String]
|
56
|
+
def verbose_summary
|
57
|
+
<<~SUMMARY
|
58
|
+
# Code Section Summary
|
59
|
+
|
60
|
+
## Resolved Variables
|
61
|
+
|
62
|
+
#{variable_summary}
|
63
|
+
|
64
|
+
## Functions
|
65
|
+
|
66
|
+
#{function_summary}
|
67
|
+
SUMMARY
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def variable_summary
|
73
|
+
return '(no variables defined)' if @variables.empty?
|
74
|
+
|
75
|
+
@variables.map { |k, v| "#{k} := #{v}" }
|
76
|
+
.join("\n")
|
77
|
+
end
|
78
|
+
|
79
|
+
def function_summary
|
80
|
+
return '(no functions defined)' if @functions.empty?
|
81
|
+
|
82
|
+
@functions.map { |k, f| "#{k}: #{f}" }
|
83
|
+
.join("\n")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|