csv_plus_plus 0.1.3 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -3
  3. data/docs/CHANGELOG.md +18 -0
  4. data/lib/csv_plus_plus/a1_reference.rb +202 -0
  5. data/lib/csv_plus_plus/benchmarked_compiler.rb +3 -3
  6. data/lib/csv_plus_plus/cell.rb +1 -35
  7. data/lib/csv_plus_plus/cli.rb +43 -80
  8. data/lib/csv_plus_plus/cli_flag.rb +77 -70
  9. data/lib/csv_plus_plus/color.rb +1 -1
  10. data/lib/csv_plus_plus/compiler.rb +31 -21
  11. data/lib/csv_plus_plus/entities/ast_builder.rb +11 -4
  12. data/lib/csv_plus_plus/entities/boolean.rb +16 -9
  13. data/lib/csv_plus_plus/entities/builtins.rb +68 -40
  14. data/lib/csv_plus_plus/entities/date.rb +14 -11
  15. data/lib/csv_plus_plus/entities/entity.rb +11 -29
  16. data/lib/csv_plus_plus/entities/entity_with_arguments.rb +18 -31
  17. data/lib/csv_plus_plus/entities/function.rb +22 -11
  18. data/lib/csv_plus_plus/entities/function_call.rb +35 -11
  19. data/lib/csv_plus_plus/entities/has_identifier.rb +19 -0
  20. data/lib/csv_plus_plus/entities/number.rb +15 -10
  21. data/lib/csv_plus_plus/entities/reference.rb +77 -0
  22. data/lib/csv_plus_plus/entities/runtime_value.rb +36 -23
  23. data/lib/csv_plus_plus/entities/string.rb +13 -10
  24. data/lib/csv_plus_plus/entities.rb +2 -18
  25. data/lib/csv_plus_plus/error/cli_error.rb +17 -0
  26. data/lib/csv_plus_plus/error/compiler_error.rb +17 -0
  27. data/lib/csv_plus_plus/error/error.rb +18 -5
  28. data/lib/csv_plus_plus/error/formula_syntax_error.rb +12 -13
  29. data/lib/csv_plus_plus/error/modifier_syntax_error.rb +10 -36
  30. data/lib/csv_plus_plus/error/modifier_validation_error.rb +6 -32
  31. data/lib/csv_plus_plus/error/positional_error.rb +15 -0
  32. data/lib/csv_plus_plus/error/writer_error.rb +1 -1
  33. data/lib/csv_plus_plus/error.rb +4 -1
  34. data/lib/csv_plus_plus/error_formatter.rb +111 -0
  35. data/lib/csv_plus_plus/google_api_client.rb +18 -8
  36. data/lib/csv_plus_plus/lexer/racc_lexer.rb +144 -0
  37. data/lib/csv_plus_plus/lexer/tokenizer.rb +53 -17
  38. data/lib/csv_plus_plus/lexer.rb +40 -1
  39. data/lib/csv_plus_plus/modifier/data_validation.rb +1 -1
  40. data/lib/csv_plus_plus/modifier/expand.rb +17 -0
  41. data/lib/csv_plus_plus/modifier.rb +6 -1
  42. data/lib/csv_plus_plus/options/file_options.rb +49 -0
  43. data/lib/csv_plus_plus/options/google_sheets_options.rb +42 -0
  44. data/lib/csv_plus_plus/options/options.rb +102 -0
  45. data/lib/csv_plus_plus/options.rb +22 -110
  46. data/lib/csv_plus_plus/parser/cell_value.tab.rb +65 -66
  47. data/lib/csv_plus_plus/parser/code_section.tab.rb +92 -84
  48. data/lib/csv_plus_plus/parser/modifier.tab.rb +40 -30
  49. data/lib/csv_plus_plus/reader/csv.rb +50 -0
  50. data/lib/csv_plus_plus/reader/google_sheets.rb +129 -0
  51. data/lib/csv_plus_plus/reader/reader.rb +27 -0
  52. data/lib/csv_plus_plus/reader/rubyxl.rb +37 -0
  53. data/lib/csv_plus_plus/reader.rb +14 -0
  54. data/lib/csv_plus_plus/runtime/graph.rb +6 -6
  55. data/lib/csv_plus_plus/runtime/{position_tracker.rb → position.rb} +16 -5
  56. data/lib/csv_plus_plus/runtime/references.rb +32 -27
  57. data/lib/csv_plus_plus/runtime/runtime.rb +73 -67
  58. data/lib/csv_plus_plus/runtime/scope.rb +280 -0
  59. data/lib/csv_plus_plus/runtime.rb +9 -9
  60. data/lib/csv_plus_plus/source_code.rb +14 -9
  61. data/lib/csv_plus_plus/template.rb +17 -12
  62. data/lib/csv_plus_plus/version.rb +1 -1
  63. data/lib/csv_plus_plus/writer/csv.rb +32 -5
  64. data/lib/csv_plus_plus/writer/excel.rb +19 -6
  65. data/lib/csv_plus_plus/writer/file_backer_upper.rb +27 -14
  66. data/lib/csv_plus_plus/writer/google_sheets.rb +23 -129
  67. data/lib/csv_plus_plus/writer/{google_sheet_builder.rb → google_sheets_builder.rb} +39 -55
  68. data/lib/csv_plus_plus/writer/merger.rb +56 -0
  69. data/lib/csv_plus_plus/writer/open_document.rb +16 -2
  70. data/lib/csv_plus_plus/writer/rubyxl_builder.rb +68 -43
  71. data/lib/csv_plus_plus/writer/writer.rb +42 -0
  72. data/lib/csv_plus_plus/writer.rb +58 -19
  73. data/lib/csv_plus_plus.rb +26 -14
  74. metadata +43 -18
  75. data/lib/csv_plus_plus/entities/cell_reference.rb +0 -231
  76. data/lib/csv_plus_plus/entities/variable.rb +0 -37
  77. data/lib/csv_plus_plus/error/syntax_error.rb +0 -71
  78. data/lib/csv_plus_plus/google_options.rb +0 -32
  79. data/lib/csv_plus_plus/lexer/lexer.rb +0 -89
  80. data/lib/csv_plus_plus/runtime/can_define_references.rb +0 -87
  81. data/lib/csv_plus_plus/runtime/can_resolve_references.rb +0 -209
  82. data/lib/csv_plus_plus/writer/base_writer.rb +0 -45
@@ -1,11 +1,15 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require_relative './writer/base_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
- runtime: ::CSVPlusPlus::Runtime::Runtime
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 runtime [Runtime] The current runtime.
35
+ # @param position [Position] The current position.
32
36
  #
33
37
  # @return [Writer::CSV | Writer::Excel | Writer::GoogleSheets | Writer::OpenDocument]
34
- # rubocop:disable Metrics/MethodLength
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 ::CSVPlusPlus::Writer::CSV.new(options, runtime)
39
- when ::CSVPlusPlus::Options::OutputFormat::Excel then ::CSVPlusPlus::Writer::Excel.new(options, runtime)
40
- when ::CSVPlusPlus::Options::OutputFormat::GoogleSheets then ::CSVPlusPlus::Writer::GoogleSheets.new(
41
- options,
42
- runtime
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
- # rubocop:enable Metrics/MethodLength
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/options'
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(input: ::String, filename: ::T.nilable(::String), options: ::CSVPlusPlus::Options).void }
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 input [String] The csvpp input to compile
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.apply_template_to_sheet!(input, filename, options)
58
- warn(options.verbose_summary) if options.verbose
61
+ def self.cli_compile(source_code, options)
62
+ runtime = ::CSVPlusPlus::Runtime.new(source_code:)
59
63
 
60
- runtime = ::CSVPlusPlus::Runtime.new(source_code: ::CSVPlusPlus::SourceCode.new(input:, filename:))
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(compiler: ::CSVPlusPlus::Compiler, options: ::CSVPlusPlus::Options, template: ::CSVPlusPlus::Template).void
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 |runtime|
80
- output = ::CSVPlusPlus::Writer.writer(options, runtime)
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.1.3
4
+ version: 0.2.1
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-04-11 00:00:00.000000000 Z
11
+ date: 2023-09-09 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/syntax_error.rb
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/lexer.rb
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,39 +181,47 @@ 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/position_tracker.rb
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
190
- homepage: https://github.com/patrickomatic/csv-plus-plus
214
+ - lib/csv_plus_plus/writer/writer.rb
215
+ homepage: https://github.com/patrickomatic/csv-plus-plus-ruby
191
216
  licenses:
192
217
  - MIT
193
218
  metadata:
194
- bug_tracker_uri: https://github.com/patrickomatic/csv-plus-plus/issues
219
+ bug_tracker_uri: https://github.com/patrickomatic/csv-plus-plus-ruby/issues
195
220
  documentation_uri: https://www.rubydoc.info/gems/csv_plus_plus/
196
- github_repo: git://github.com/patrickomatic/csv-plus-plus
197
- homepage_uri: https://github.com/patrickomatic/csv-plus-plus
198
- source_code_uri: https://github.com/patrickomatic/csv-plus-plus
199
- changelog_uri: https://github.com/patrickomatic/csv-plus-plus/blob/main/docs/CHANGELOG.md
221
+ github_repo: git://github.com/patrickomatic/csv-plus-plus-ruby
222
+ homepage_uri: https://github.com/patrickomatic/csv-plus-plus-ruby
223
+ source_code_uri: https://github.com/patrickomatic/csv-plus-plus-ruby
224
+ changelog_uri: https://github.com/patrickomatic/csv-plus-plus-ruby/blob/main/docs/CHANGELOG.md
200
225
  rubygems_mfa_required: 'true'
201
226
  post_install_message:
202
227
  rdoc_options: []
@@ -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