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.
- checksums.yaml +4 -4
- data/README.md +8 -3
- data/docs/CHANGELOG.md +16 -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 +71 -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 +97 -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 +31 -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 +37 -12
- 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
data/lib/csv_plus_plus/writer.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require_relative './writer/
|
4
|
+
require_relative './writer/merger'
|
5
|
+
require_relative './writer/writer'
|
6
|
+
|
5
7
|
require_relative './writer/csv'
|
6
8
|
require_relative './writer/excel'
|
7
9
|
require_relative './writer/google_sheets'
|
10
|
+
require_relative './writer/google_sheets_builder'
|
8
11
|
require_relative './writer/open_document'
|
12
|
+
require_relative './writer/rubyxl_builder'
|
9
13
|
|
10
14
|
module CSVPlusPlus
|
11
15
|
# Various strategies for writing to various formats (excel, google sheets, CSV & OpenDocument (not yet implemented))
|
@@ -14,8 +18,8 @@ module CSVPlusPlus
|
|
14
18
|
|
15
19
|
sig do
|
16
20
|
params(
|
17
|
-
options: ::CSVPlusPlus::Options,
|
18
|
-
|
21
|
+
options: ::CSVPlusPlus::Options::Options,
|
22
|
+
position: ::CSVPlusPlus::Runtime::Position
|
19
23
|
).returns(
|
20
24
|
::T.any(
|
21
25
|
::CSVPlusPlus::Writer::CSV,
|
@@ -28,27 +32,62 @@ module CSVPlusPlus
|
|
28
32
|
# Return an instance of a writer depending on the given +options+
|
29
33
|
#
|
30
34
|
# @param options [Options] The supplied options.
|
31
|
-
# @param
|
35
|
+
# @param position [Position] The current position.
|
32
36
|
#
|
33
37
|
# @return [Writer::CSV | Writer::Excel | Writer::GoogleSheets | Writer::OpenDocument]
|
34
|
-
|
35
|
-
def self.writer(options, runtime)
|
38
|
+
def self.writer(options, position)
|
36
39
|
output_format = options.output_format
|
37
40
|
case output_format
|
38
|
-
when ::CSVPlusPlus::Options::OutputFormat::CSV then
|
39
|
-
when ::CSVPlusPlus::Options::OutputFormat::Excel then
|
40
|
-
when ::CSVPlusPlus::Options::OutputFormat::GoogleSheets then
|
41
|
-
|
42
|
-
|
43
|
-
)
|
44
|
-
when ::CSVPlusPlus::Options::OutputFormat::OpenDocument then ::CSVPlusPlus::Writer::OpenDocument.new(
|
45
|
-
options,
|
46
|
-
runtime
|
47
|
-
)
|
48
|
-
else
|
49
|
-
::T.absurd(output_format)
|
41
|
+
when ::CSVPlusPlus::Options::OutputFormat::CSV then csv(options, position)
|
42
|
+
when ::CSVPlusPlus::Options::OutputFormat::Excel then excel(options, position)
|
43
|
+
when ::CSVPlusPlus::Options::OutputFormat::GoogleSheets then google_sheets(options, position)
|
44
|
+
when ::CSVPlusPlus::Options::OutputFormat::OpenDocument then open_document(options, position)
|
45
|
+
else ::T.absurd(output_format)
|
50
46
|
end
|
51
47
|
end
|
52
|
-
|
48
|
+
|
49
|
+
sig do
|
50
|
+
params(
|
51
|
+
options: ::CSVPlusPlus::Options::Options,
|
52
|
+
position: ::CSVPlusPlus::Runtime::Position
|
53
|
+
).returns(::CSVPlusPlus::Writer::CSV)
|
54
|
+
end
|
55
|
+
# Instantiate a +CSV+ writer
|
56
|
+
def self.csv(options, position)
|
57
|
+
::CSVPlusPlus::Writer::CSV.new(::T.cast(options, ::CSVPlusPlus::Options::FileOptions), position)
|
58
|
+
end
|
59
|
+
|
60
|
+
sig do
|
61
|
+
params(
|
62
|
+
options: ::CSVPlusPlus::Options::Options,
|
63
|
+
position: ::CSVPlusPlus::Runtime::Position
|
64
|
+
).returns(::CSVPlusPlus::Writer::Excel)
|
65
|
+
end
|
66
|
+
# Instantiate a +Excel+ writer
|
67
|
+
def self.excel(options, position)
|
68
|
+
::CSVPlusPlus::Writer::Excel.new(::T.cast(options, ::CSVPlusPlus::Options::FileOptions), position)
|
69
|
+
end
|
70
|
+
|
71
|
+
sig do
|
72
|
+
params(
|
73
|
+
options: ::CSVPlusPlus::Options::Options,
|
74
|
+
position: ::CSVPlusPlus::Runtime::Position
|
75
|
+
).returns(::CSVPlusPlus::Writer::GoogleSheets)
|
76
|
+
end
|
77
|
+
# Instantiate a +GoogleSheets+ writer
|
78
|
+
def self.google_sheets(options, position)
|
79
|
+
::CSVPlusPlus::Writer::GoogleSheets.new(::T.cast(options, ::CSVPlusPlus::Options::GoogleSheetsOptions), position)
|
80
|
+
end
|
81
|
+
|
82
|
+
sig do
|
83
|
+
params(
|
84
|
+
options: ::CSVPlusPlus::Options::Options,
|
85
|
+
position: ::CSVPlusPlus::Runtime::Position
|
86
|
+
).returns(::CSVPlusPlus::Writer::OpenDocument)
|
87
|
+
end
|
88
|
+
# Instantiate an +OpenDocument+ writer
|
89
|
+
def self.open_document(options, position)
|
90
|
+
::CSVPlusPlus::Writer::OpenDocument.new(::T.cast(options, ::CSVPlusPlus::Options::FileOptions), position)
|
91
|
+
end
|
53
92
|
end
|
54
93
|
end
|
data/lib/csv_plus_plus.rb
CHANGED
@@ -14,18 +14,24 @@ require 'pathname'
|
|
14
14
|
require 'rubyXL'
|
15
15
|
require 'rubyXL/convenience_methods'
|
16
16
|
require 'set'
|
17
|
+
require 'strscan'
|
17
18
|
require 'tempfile'
|
18
19
|
|
20
|
+
require_relative 'csv_plus_plus/a1_reference'
|
21
|
+
require_relative 'csv_plus_plus/google_api_client'
|
22
|
+
require_relative 'csv_plus_plus/options'
|
23
|
+
require_relative 'csv_plus_plus/runtime/position'
|
19
24
|
require_relative 'csv_plus_plus/source_code'
|
20
25
|
|
21
|
-
require_relative 'csv_plus_plus/runtime'
|
22
|
-
|
23
26
|
require_relative 'csv_plus_plus/cli_flag'
|
24
27
|
require_relative 'csv_plus_plus/entities'
|
25
28
|
require_relative 'csv_plus_plus/error'
|
29
|
+
require_relative 'csv_plus_plus/error_formatter'
|
26
30
|
|
27
|
-
require_relative 'csv_plus_plus/cell'
|
28
31
|
require_relative 'csv_plus_plus/cli'
|
32
|
+
require_relative 'csv_plus_plus/runtime'
|
33
|
+
|
34
|
+
require_relative 'csv_plus_plus/cell'
|
29
35
|
require_relative 'csv_plus_plus/color'
|
30
36
|
require_relative 'csv_plus_plus/modifier'
|
31
37
|
|
@@ -35,9 +41,8 @@ require_relative 'csv_plus_plus/parser/modifier.tab'
|
|
35
41
|
|
36
42
|
require_relative 'csv_plus_plus/compiler'
|
37
43
|
|
38
|
-
require_relative 'csv_plus_plus/google_options'
|
39
44
|
require_relative 'csv_plus_plus/lexer'
|
40
|
-
require_relative 'csv_plus_plus/
|
45
|
+
require_relative 'csv_plus_plus/reader'
|
41
46
|
require_relative 'csv_plus_plus/row'
|
42
47
|
require_relative 'csv_plus_plus/template'
|
43
48
|
require_relative 'csv_plus_plus/writer'
|
@@ -48,16 +53,15 @@ require_relative 'csv_plus_plus/benchmarked_compiler'
|
|
48
53
|
module CSVPlusPlus
|
49
54
|
extend ::T::Sig
|
50
55
|
|
51
|
-
sig { params(
|
56
|
+
sig { params(source_code: ::CSVPlusPlus::SourceCode, options: ::CSVPlusPlus::Options::Options).void }
|
52
57
|
# Parse the input into a +Template+ and write it to the desired format
|
53
58
|
#
|
54
|
-
# @param
|
55
|
-
# @param filename [String, nil] The filename the input was read from. +nil+ if it is read from stdin.
|
59
|
+
# @param source_code [SourceCode] The source code being compiled
|
56
60
|
# @param options [Options] The various options to compile with
|
57
|
-
def self.
|
58
|
-
|
61
|
+
def self.cli_compile(source_code, options)
|
62
|
+
runtime = ::CSVPlusPlus::Runtime.new(source_code:)
|
59
63
|
|
60
|
-
|
64
|
+
warn(options.verbose_summary) if options.verbose
|
61
65
|
|
62
66
|
::CSVPlusPlus::Compiler.with_compiler(options:, runtime:) do |compiler|
|
63
67
|
template = compiler.compile_template
|
@@ -65,10 +69,18 @@ module CSVPlusPlus
|
|
65
69
|
|
66
70
|
write_template(template:, compiler:, options:)
|
67
71
|
end
|
72
|
+
rescue ::StandardError => e
|
73
|
+
::CSVPlusPlus::ErrorFormatter.new(runtime: ::T.must(runtime), options:).handle_error(e)
|
74
|
+
# the caller will exit(1)
|
75
|
+
raise(e)
|
68
76
|
end
|
69
77
|
|
70
78
|
sig do
|
71
|
-
params(
|
79
|
+
params(
|
80
|
+
compiler: ::CSVPlusPlus::Compiler,
|
81
|
+
options: ::CSVPlusPlus::Options::Options,
|
82
|
+
template: ::CSVPlusPlus::Template
|
83
|
+
).void
|
72
84
|
end
|
73
85
|
# Write the results (and possibly make a backup) of a compiled +template+
|
74
86
|
#
|
@@ -76,8 +88,8 @@ module CSVPlusPlus
|
|
76
88
|
# @param options [Options] The options we're running with
|
77
89
|
# @param template [Template] The compiled template
|
78
90
|
def self.write_template(compiler:, options:, template:)
|
79
|
-
compiler.outputting! do |
|
80
|
-
output = ::CSVPlusPlus::Writer.writer(options,
|
91
|
+
compiler.outputting! do |position|
|
92
|
+
output = ::CSVPlusPlus::Writer.writer(options, position)
|
81
93
|
output.write_backup if options.backup
|
82
94
|
output.write(template)
|
83
95
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: csv_plus_plus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Patrick Carroll
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: google-apis-drive_v3
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '3.4'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sorbet-runtime
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.5'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.5'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: bundler
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -123,6 +137,7 @@ files:
|
|
123
137
|
- bin/csvpp
|
124
138
|
- docs/CHANGELOG.md
|
125
139
|
- lib/csv_plus_plus.rb
|
140
|
+
- lib/csv_plus_plus/a1_reference.rb
|
126
141
|
- lib/csv_plus_plus/benchmarked_compiler.rb
|
127
142
|
- lib/csv_plus_plus/cell.rb
|
128
143
|
- lib/csv_plus_plus/cli.rb
|
@@ -133,27 +148,29 @@ files:
|
|
133
148
|
- lib/csv_plus_plus/entities/ast_builder.rb
|
134
149
|
- lib/csv_plus_plus/entities/boolean.rb
|
135
150
|
- lib/csv_plus_plus/entities/builtins.rb
|
136
|
-
- lib/csv_plus_plus/entities/cell_reference.rb
|
137
151
|
- lib/csv_plus_plus/entities/date.rb
|
138
152
|
- lib/csv_plus_plus/entities/entity.rb
|
139
153
|
- lib/csv_plus_plus/entities/entity_with_arguments.rb
|
140
154
|
- lib/csv_plus_plus/entities/function.rb
|
141
155
|
- lib/csv_plus_plus/entities/function_call.rb
|
156
|
+
- lib/csv_plus_plus/entities/has_identifier.rb
|
142
157
|
- lib/csv_plus_plus/entities/number.rb
|
158
|
+
- lib/csv_plus_plus/entities/reference.rb
|
143
159
|
- lib/csv_plus_plus/entities/runtime_value.rb
|
144
160
|
- lib/csv_plus_plus/entities/string.rb
|
145
|
-
- lib/csv_plus_plus/entities/variable.rb
|
146
161
|
- lib/csv_plus_plus/error.rb
|
162
|
+
- lib/csv_plus_plus/error/cli_error.rb
|
163
|
+
- lib/csv_plus_plus/error/compiler_error.rb
|
147
164
|
- lib/csv_plus_plus/error/error.rb
|
148
165
|
- lib/csv_plus_plus/error/formula_syntax_error.rb
|
149
166
|
- lib/csv_plus_plus/error/modifier_syntax_error.rb
|
150
167
|
- lib/csv_plus_plus/error/modifier_validation_error.rb
|
151
|
-
- lib/csv_plus_plus/error/
|
168
|
+
- lib/csv_plus_plus/error/positional_error.rb
|
152
169
|
- lib/csv_plus_plus/error/writer_error.rb
|
170
|
+
- lib/csv_plus_plus/error_formatter.rb
|
153
171
|
- lib/csv_plus_plus/google_api_client.rb
|
154
|
-
- lib/csv_plus_plus/google_options.rb
|
155
172
|
- lib/csv_plus_plus/lexer.rb
|
156
|
-
- lib/csv_plus_plus/lexer/
|
173
|
+
- lib/csv_plus_plus/lexer/racc_lexer.rb
|
157
174
|
- lib/csv_plus_plus/lexer/tokenizer.rb
|
158
175
|
- lib/csv_plus_plus/modifier.rb
|
159
176
|
- lib/csv_plus_plus/modifier/conditional_formatting.rb
|
@@ -164,29 +181,37 @@ files:
|
|
164
181
|
- lib/csv_plus_plus/modifier/modifier_validator.rb
|
165
182
|
- lib/csv_plus_plus/modifier/rubyxl_modifier.rb
|
166
183
|
- lib/csv_plus_plus/options.rb
|
184
|
+
- lib/csv_plus_plus/options/file_options.rb
|
185
|
+
- lib/csv_plus_plus/options/google_sheets_options.rb
|
186
|
+
- lib/csv_plus_plus/options/options.rb
|
167
187
|
- lib/csv_plus_plus/parser/cell_value.tab.rb
|
168
188
|
- lib/csv_plus_plus/parser/code_section.tab.rb
|
169
189
|
- lib/csv_plus_plus/parser/modifier.tab.rb
|
190
|
+
- lib/csv_plus_plus/reader.rb
|
191
|
+
- lib/csv_plus_plus/reader/csv.rb
|
192
|
+
- lib/csv_plus_plus/reader/google_sheets.rb
|
193
|
+
- lib/csv_plus_plus/reader/reader.rb
|
194
|
+
- lib/csv_plus_plus/reader/rubyxl.rb
|
170
195
|
- lib/csv_plus_plus/row.rb
|
171
196
|
- lib/csv_plus_plus/runtime.rb
|
172
|
-
- lib/csv_plus_plus/runtime/can_define_references.rb
|
173
|
-
- lib/csv_plus_plus/runtime/can_resolve_references.rb
|
174
197
|
- lib/csv_plus_plus/runtime/graph.rb
|
175
|
-
- lib/csv_plus_plus/runtime/
|
198
|
+
- lib/csv_plus_plus/runtime/position.rb
|
176
199
|
- lib/csv_plus_plus/runtime/references.rb
|
177
200
|
- lib/csv_plus_plus/runtime/runtime.rb
|
201
|
+
- lib/csv_plus_plus/runtime/scope.rb
|
178
202
|
- lib/csv_plus_plus/source_code.rb
|
179
203
|
- lib/csv_plus_plus/template.rb
|
180
204
|
- lib/csv_plus_plus/version.rb
|
181
205
|
- lib/csv_plus_plus/writer.rb
|
182
|
-
- lib/csv_plus_plus/writer/base_writer.rb
|
183
206
|
- lib/csv_plus_plus/writer/csv.rb
|
184
207
|
- lib/csv_plus_plus/writer/excel.rb
|
185
208
|
- lib/csv_plus_plus/writer/file_backer_upper.rb
|
186
|
-
- lib/csv_plus_plus/writer/google_sheet_builder.rb
|
187
209
|
- lib/csv_plus_plus/writer/google_sheets.rb
|
210
|
+
- lib/csv_plus_plus/writer/google_sheets_builder.rb
|
211
|
+
- lib/csv_plus_plus/writer/merger.rb
|
188
212
|
- lib/csv_plus_plus/writer/open_document.rb
|
189
213
|
- lib/csv_plus_plus/writer/rubyxl_builder.rb
|
214
|
+
- lib/csv_plus_plus/writer/writer.rb
|
190
215
|
homepage: https://github.com/patrickomatic/csv-plus-plus
|
191
216
|
licenses:
|
192
217
|
- MIT
|
@@ -1,231 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
module CSVPlusPlus
|
5
|
-
module Entities
|
6
|
-
# A reference to a cell. Internally it is represented by a simple +cell_index+ and +row_index+ but there are
|
7
|
-
# functions for converting to and from A1-style formats. Supported formats are:
|
8
|
-
#
|
9
|
-
# * `1` - A reference to the entire first row
|
10
|
-
# * `A` - A reference to the entire first column
|
11
|
-
# * `A1` - A reference to the first cell (top left)
|
12
|
-
# * `A1:D10` - The range defined between A1 and D10
|
13
|
-
# * `Sheet1!B2` - Cell B2 on the sheet "Sheet1"
|
14
|
-
#
|
15
|
-
# @attr sheet_name [String, nil] The name of the sheet reference
|
16
|
-
# @attr_reader cell_index [Integer, nil] The cell index of the cell being referenced
|
17
|
-
# @attr_reader row_index [Integer, nil] The row index of the cell being referenced
|
18
|
-
# @attr_reader scoped_to_expand [Expand, nil] If set, the expand in which this variable is scoped to. It cannot be
|
19
|
-
# resolved outside of the given expand.
|
20
|
-
# @attr_reader upper_cell_index [Integer, nil] If set, the cell reference is a range and this is the upper cell
|
21
|
-
# index of it
|
22
|
-
# @attr_reader upper_row_index [Integer, nil] If set, the cell reference is a range and this is the upper row index
|
23
|
-
# of it
|
24
|
-
# rubocop:disable Metrics/ClassLength
|
25
|
-
class CellReference < Entity
|
26
|
-
extend ::T::Sig
|
27
|
-
|
28
|
-
sig { returns(::T.nilable(::String)) }
|
29
|
-
attr_accessor :sheet_name
|
30
|
-
|
31
|
-
sig { returns(::T.nilable(::Integer)) }
|
32
|
-
attr_reader :cell_index
|
33
|
-
|
34
|
-
sig { returns(::T.nilable(::Integer)) }
|
35
|
-
attr_reader :row_index
|
36
|
-
|
37
|
-
sig { returns(::T.nilable(::CSVPlusPlus::Modifier::Expand)) }
|
38
|
-
attr_reader :scoped_to_expand
|
39
|
-
|
40
|
-
sig { returns(::T.nilable(::Integer)) }
|
41
|
-
attr_reader :upper_cell_index
|
42
|
-
|
43
|
-
sig { returns(::T.nilable(::Integer)) }
|
44
|
-
attr_reader :upper_row_index
|
45
|
-
|
46
|
-
# TODO: this is getting gross, maybe define an actual parser
|
47
|
-
A1_NOTATION_REGEXP = /
|
48
|
-
^
|
49
|
-
(?:
|
50
|
-
(?:
|
51
|
-
(?:'([^'\\]|\\.)*') # allow for a single-quoted sheet name
|
52
|
-
|
|
53
|
-
(\w+) # or if it's not quoted, just allow \w+
|
54
|
-
)
|
55
|
-
! # if a sheet name is specified, it's always followed by a !
|
56
|
-
)?
|
57
|
-
([a-zA-Z0-9]+) # the only part required - something alphanumeric
|
58
|
-
(?: :([a-zA-Z0-9]+))? # and they might make it a range
|
59
|
-
$
|
60
|
-
/x
|
61
|
-
public_constant :A1_NOTATION_REGEXP
|
62
|
-
|
63
|
-
ALPHA = ::T.let(('A'..'Z').to_a.freeze, ::T::Array[::String])
|
64
|
-
private_constant :ALPHA
|
65
|
-
|
66
|
-
sig { params(cell_reference_string: ::String).returns(::T::Boolean) }
|
67
|
-
# Does the given +cell_reference_string+ conform to a valid cell reference?
|
68
|
-
#
|
69
|
-
# {https://developers.google.com/sheets/api/guides/concepts}
|
70
|
-
#
|
71
|
-
# @param cell_reference_string [::String] The string to check if it is a valid cell reference (we assume it's in
|
72
|
-
# A1 notation but maybe can support R1C1)
|
73
|
-
#
|
74
|
-
# @return [::T::Boolean]
|
75
|
-
def self.valid_cell_reference?(cell_reference_string)
|
76
|
-
!(cell_reference_string =~ ::CSVPlusPlus::Entities::CellReference::A1_NOTATION_REGEXP).nil?
|
77
|
-
end
|
78
|
-
|
79
|
-
sig do
|
80
|
-
params(
|
81
|
-
cell_index: ::T.nilable(::Integer),
|
82
|
-
ref: ::T.nilable(::String),
|
83
|
-
row_index: ::T.nilable(::Integer),
|
84
|
-
scoped_to_expand: ::T.nilable(::CSVPlusPlus::Modifier::Expand)
|
85
|
-
).void
|
86
|
-
end
|
87
|
-
# Either +ref+, +cell_index+ or +row_index+ must be specified.
|
88
|
-
#
|
89
|
-
# @param cell_index [Integer, nil] The index of the cell being referenced.
|
90
|
-
# @param ref [Integer, nil] An A1-style cell reference (that will be parsed into it's row/cell indexes).
|
91
|
-
# @param row_index [Integer, nil] The index of the row being referenced.
|
92
|
-
# @param scoped_to_expand [Expand] The [[expand]] that this cell reference will be scoped to. In other words, it
|
93
|
-
# will only be able to be resolved if the runtime is within the bounds of the expand (it can't be referenced
|
94
|
-
# outside of the expand.)
|
95
|
-
# rubocop:disable Metrics/MethodLength
|
96
|
-
def initialize(cell_index: nil, ref: nil, row_index: nil, scoped_to_expand: nil)
|
97
|
-
raise(::ArgumentError, 'Must specify :ref, :cell_index or :row_index') unless ref || cell_index || row_index
|
98
|
-
|
99
|
-
super(::CSVPlusPlus::Entities::Type::CellReference)
|
100
|
-
|
101
|
-
if ref
|
102
|
-
from_a1_ref!(ref)
|
103
|
-
else
|
104
|
-
@cell_index = ::T.let(cell_index, ::T.nilable(::Integer))
|
105
|
-
@row_index = ::T.let(row_index, ::T.nilable(::Integer))
|
106
|
-
|
107
|
-
@upper_cell_index = ::T.let(nil, ::T.nilable(::Integer))
|
108
|
-
@upper_row_index = ::T.let(nil, ::T.nilable(::Integer))
|
109
|
-
end
|
110
|
-
|
111
|
-
@scoped_to_expand = scoped_to_expand
|
112
|
-
end
|
113
|
-
# rubocop:enable Metrics/MethodLength
|
114
|
-
|
115
|
-
sig { override.params(other: ::CSVPlusPlus::Entities::Entity).returns(::T::Boolean) }
|
116
|
-
# @param other [Entity]
|
117
|
-
#
|
118
|
-
# @return [boolean]
|
119
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
120
|
-
def ==(other)
|
121
|
-
return false unless super
|
122
|
-
|
123
|
-
other.is_a?(self.class) && @cell_index == other.cell_index && @row_index == other.row_index \
|
124
|
-
&& @sheet_name == other.sheet_name && @scoped_to_expand == other.scoped_to_expand \
|
125
|
-
&& @upper_cell_index == other.upper_cell_index && @upper_row_index == other.upper_row_index
|
126
|
-
end
|
127
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
128
|
-
|
129
|
-
sig { override.params(runtime: ::CSVPlusPlus::Runtime::Runtime).returns(::String) }
|
130
|
-
# Get the A1-style cell reference
|
131
|
-
#
|
132
|
-
# @param runtime [Runtime] The current runtime
|
133
|
-
#
|
134
|
-
# @return [::String] An A1-style reference
|
135
|
-
def evaluate(runtime)
|
136
|
-
# unless in_scope?(runtime)
|
137
|
-
# runtime.raise_modifier_syntax_error(message: 'Reference is out of scope', bad_input: runtime.cell.value)
|
138
|
-
# end
|
139
|
-
|
140
|
-
to_a1_ref(runtime) || ''
|
141
|
-
end
|
142
|
-
|
143
|
-
sig { returns(::T::Boolean) }
|
144
|
-
# Is the cell_reference a range? - something like A1:D10
|
145
|
-
#
|
146
|
-
# @return [boolean]
|
147
|
-
def range?
|
148
|
-
!upper_row_index.nil? || !upper_cell_index.nil?
|
149
|
-
end
|
150
|
-
|
151
|
-
private
|
152
|
-
|
153
|
-
sig { params(runtime: ::CSVPlusPlus::Runtime::Runtime).returns(::T.nilable(::String)) }
|
154
|
-
# Turns index-based/X,Y coordinates into a A1 format
|
155
|
-
#
|
156
|
-
# @param runtime [Runtime]
|
157
|
-
#
|
158
|
-
# @return [::String, nil]
|
159
|
-
def to_a1_ref(runtime)
|
160
|
-
row_index = runtime_row_index(runtime)
|
161
|
-
return unless row_index || @cell_index
|
162
|
-
|
163
|
-
rowref = row_index ? (row_index + 1).to_s : ''
|
164
|
-
cellref = @cell_index ? to_a1_cell_ref : ''
|
165
|
-
[cellref, rowref].join
|
166
|
-
end
|
167
|
-
|
168
|
-
sig { params(runtime: ::CSVPlusPlus::Runtime::Runtime).returns(::T.nilable(::Integer)) }
|
169
|
-
def runtime_row_index(runtime)
|
170
|
-
@scoped_to_expand ? runtime.row_index : @row_index
|
171
|
-
end
|
172
|
-
|
173
|
-
sig { returns(::String) }
|
174
|
-
# Turns a cell index into an A1 reference (just the "A" part - for example 0 == 'A', 1 == 'B', 2 == 'C', etc.)
|
175
|
-
#
|
176
|
-
# @return [::String]
|
177
|
-
def to_a1_cell_ref
|
178
|
-
c = @cell_index.dup
|
179
|
-
ref = ''
|
180
|
-
|
181
|
-
while c >= 0
|
182
|
-
# rubocop:disable Lint/ConstantResolution
|
183
|
-
ref += ::T.must(ALPHA[c % 26])
|
184
|
-
# rubocop:enable Lint/ConstantResolution
|
185
|
-
c = (c / 26).floor - 1
|
186
|
-
end
|
187
|
-
|
188
|
-
ref.reverse
|
189
|
-
end
|
190
|
-
|
191
|
-
sig { params(ref: ::String).void }
|
192
|
-
def from_a1_ref!(ref)
|
193
|
-
quoted_sheet_name, unquoted_sheet_name, lower_range, upper_range = ::T.must(
|
194
|
-
ref.strip.match(
|
195
|
-
::CSVPlusPlus::Entities::CellReference::A1_NOTATION_REGEXP
|
196
|
-
)
|
197
|
-
).captures
|
198
|
-
|
199
|
-
@sheet_name = quoted_sheet_name || unquoted_sheet_name
|
200
|
-
|
201
|
-
parse_lower_range!(lower_range) if lower_range
|
202
|
-
parse_upper_range!(upper_range) if upper_range
|
203
|
-
end
|
204
|
-
|
205
|
-
sig { params(lower_range: ::String).void }
|
206
|
-
def parse_lower_range!(lower_range)
|
207
|
-
cell_ref, row_ref = ::T.must(lower_range.match(/^([a-zA-Z]+)?(\d+)?$/)).captures
|
208
|
-
@cell_index = from_a1_cell_ref!(cell_ref) if cell_ref
|
209
|
-
@row_index = Integer(row_ref, 10) - 1 if row_ref
|
210
|
-
end
|
211
|
-
|
212
|
-
sig { params(upper_range: ::String).void }
|
213
|
-
# TODO: make this less redundant with the above function
|
214
|
-
def parse_upper_range!(upper_range)
|
215
|
-
cell_ref, row_ref = ::T.must(upper_range.match(/^([a-zA-Z]+)?(\d+)?$/)).captures
|
216
|
-
@upper_cell_index = from_a1_cell_ref!(cell_ref) if cell_ref
|
217
|
-
@upper_row_index = Integer(row_ref, 10) - 1 if row_ref
|
218
|
-
end
|
219
|
-
|
220
|
-
sig { params(cell_ref: ::String).returns(::Integer) }
|
221
|
-
def from_a1_cell_ref!(cell_ref)
|
222
|
-
(cell_ref.upcase.chars.reduce(0) do |cell_index, letter|
|
223
|
-
# rubocop:disable Lint/ConstantResolution
|
224
|
-
(cell_index * 26) + ::T.must(ALPHA.find_index(letter)) + 1
|
225
|
-
# rubocop:enable Lint/ConstantResolution
|
226
|
-
end) - 1
|
227
|
-
end
|
228
|
-
end
|
229
|
-
# rubocop:enable Metrics/ClassLength
|
230
|
-
end
|
231
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
module CSVPlusPlus
|
5
|
-
module Entities
|
6
|
-
# TODO: get rid of this I think - everything will just be References
|
7
|
-
#
|
8
|
-
# A reference to a variable
|
9
|
-
class Variable < Entity
|
10
|
-
extend ::T::Sig
|
11
|
-
|
12
|
-
sig { params(id: ::Symbol).void }
|
13
|
-
# @param id [Symbol] The identifier of the variable
|
14
|
-
def initialize(id)
|
15
|
-
super(::CSVPlusPlus::Entities::Type::Variable, id:)
|
16
|
-
end
|
17
|
-
|
18
|
-
sig { override.params(_runtime: ::CSVPlusPlus::Runtime::Runtime).returns(::String) }
|
19
|
-
# @param _runtime [Runtime]
|
20
|
-
#
|
21
|
-
# @return [::String]
|
22
|
-
def evaluate(_runtime)
|
23
|
-
"$$#{@id}"
|
24
|
-
end
|
25
|
-
|
26
|
-
sig { override.params(other: ::CSVPlusPlus::Entities::Entity).returns(::T::Boolean) }
|
27
|
-
# @param other [Entity]
|
28
|
-
#
|
29
|
-
# @return [boolean]
|
30
|
-
def ==(other)
|
31
|
-
return false unless super
|
32
|
-
|
33
|
-
other.is_a?(self.class) && @id == other.id
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,71 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
module CSVPlusPlus
|
5
|
-
module Error
|
6
|
-
# An error that can be thrown for various syntax errors
|
7
|
-
class SyntaxError < ::CSVPlusPlus::Error::Error
|
8
|
-
extend ::T::Sig
|
9
|
-
|
10
|
-
sig { params(runtime: ::CSVPlusPlus::Runtime::Runtime, wrapped_error: ::T.nilable(::StandardError)).void }
|
11
|
-
# @param runtime [Runtime] The current runtime
|
12
|
-
# @param wrapped_error [StandardError] The underlying error that caused the syntax error. For example a
|
13
|
-
# Racc::ParseError that was thrown
|
14
|
-
def initialize(runtime, wrapped_error: nil)
|
15
|
-
@runtime = runtime
|
16
|
-
@wrapped_error = wrapped_error
|
17
|
-
|
18
|
-
super()
|
19
|
-
end
|
20
|
-
|
21
|
-
sig { returns(::String) }
|
22
|
-
# @return [::String]
|
23
|
-
def to_s
|
24
|
-
to_trace
|
25
|
-
end
|
26
|
-
|
27
|
-
# TODO: clean up all these different string-formatting error classes
|
28
|
-
sig { override.returns(::String) }
|
29
|
-
# @return [::String]
|
30
|
-
def error_message
|
31
|
-
''
|
32
|
-
end
|
33
|
-
|
34
|
-
sig { returns(::String) }
|
35
|
-
# Output a verbose user-helpful string that references the current runtime
|
36
|
-
def to_verbose_trace
|
37
|
-
warn(@wrapped_error.full_message) if @wrapped_error
|
38
|
-
warn((@wrapped_error.backtrace || []).join("\n")) if @wrapped_error&.backtrace
|
39
|
-
to_trace
|
40
|
-
end
|
41
|
-
|
42
|
-
sig { returns(::String) }
|
43
|
-
# Output a user-helpful string that references the runtime state
|
44
|
-
#
|
45
|
-
# @return [String]
|
46
|
-
def to_trace
|
47
|
-
"#{message_prefix}#{cell_index} #{error_message}"
|
48
|
-
end
|
49
|
-
|
50
|
-
private
|
51
|
-
|
52
|
-
sig { returns(::String) }
|
53
|
-
def cell_index
|
54
|
-
if @runtime.parsing_csv_section?
|
55
|
-
"[#{@runtime.row_index},#{@runtime.cell_index}]"
|
56
|
-
else
|
57
|
-
''
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
sig { returns(::String) }
|
62
|
-
def message_prefix
|
63
|
-
line_number = @runtime.line_number
|
64
|
-
filename = @runtime.source_code.filename
|
65
|
-
|
66
|
-
line_str = ":#{line_number}"
|
67
|
-
"#{filename}#{line_str}"
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|