csv_plus_plus 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -3
  3. data/docs/CHANGELOG.md +16 -0
  4. data/lib/csv_plus_plus/a1_reference.rb +202 -0
  5. data/lib/csv_plus_plus/benchmarked_compiler.rb +3 -3
  6. data/lib/csv_plus_plus/cell.rb +1 -35
  7. data/lib/csv_plus_plus/cli.rb +43 -80
  8. data/lib/csv_plus_plus/cli_flag.rb +71 -70
  9. data/lib/csv_plus_plus/color.rb +1 -1
  10. data/lib/csv_plus_plus/compiler.rb +31 -21
  11. data/lib/csv_plus_plus/entities/ast_builder.rb +11 -4
  12. data/lib/csv_plus_plus/entities/boolean.rb +16 -9
  13. data/lib/csv_plus_plus/entities/builtins.rb +68 -40
  14. data/lib/csv_plus_plus/entities/date.rb +14 -11
  15. data/lib/csv_plus_plus/entities/entity.rb +11 -29
  16. data/lib/csv_plus_plus/entities/entity_with_arguments.rb +18 -31
  17. data/lib/csv_plus_plus/entities/function.rb +22 -11
  18. data/lib/csv_plus_plus/entities/function_call.rb +35 -11
  19. data/lib/csv_plus_plus/entities/has_identifier.rb +19 -0
  20. data/lib/csv_plus_plus/entities/number.rb +15 -10
  21. data/lib/csv_plus_plus/entities/reference.rb +77 -0
  22. data/lib/csv_plus_plus/entities/runtime_value.rb +36 -23
  23. data/lib/csv_plus_plus/entities/string.rb +13 -10
  24. data/lib/csv_plus_plus/entities.rb +2 -18
  25. data/lib/csv_plus_plus/error/cli_error.rb +17 -0
  26. data/lib/csv_plus_plus/error/compiler_error.rb +17 -0
  27. data/lib/csv_plus_plus/error/error.rb +18 -5
  28. data/lib/csv_plus_plus/error/formula_syntax_error.rb +12 -13
  29. data/lib/csv_plus_plus/error/modifier_syntax_error.rb +10 -36
  30. data/lib/csv_plus_plus/error/modifier_validation_error.rb +6 -32
  31. data/lib/csv_plus_plus/error/positional_error.rb +15 -0
  32. data/lib/csv_plus_plus/error/writer_error.rb +1 -1
  33. data/lib/csv_plus_plus/error.rb +4 -1
  34. data/lib/csv_plus_plus/error_formatter.rb +111 -0
  35. data/lib/csv_plus_plus/google_api_client.rb +18 -8
  36. data/lib/csv_plus_plus/lexer/racc_lexer.rb +144 -0
  37. data/lib/csv_plus_plus/lexer/tokenizer.rb +53 -17
  38. data/lib/csv_plus_plus/lexer.rb +40 -1
  39. data/lib/csv_plus_plus/modifier/data_validation.rb +1 -1
  40. data/lib/csv_plus_plus/modifier/expand.rb +17 -0
  41. data/lib/csv_plus_plus/modifier.rb +6 -1
  42. data/lib/csv_plus_plus/options/file_options.rb +49 -0
  43. data/lib/csv_plus_plus/options/google_sheets_options.rb +42 -0
  44. data/lib/csv_plus_plus/options/options.rb +97 -0
  45. data/lib/csv_plus_plus/options.rb +22 -110
  46. data/lib/csv_plus_plus/parser/cell_value.tab.rb +65 -66
  47. data/lib/csv_plus_plus/parser/code_section.tab.rb +92 -84
  48. data/lib/csv_plus_plus/parser/modifier.tab.rb +40 -30
  49. data/lib/csv_plus_plus/reader/csv.rb +50 -0
  50. data/lib/csv_plus_plus/reader/google_sheets.rb +129 -0
  51. data/lib/csv_plus_plus/reader/reader.rb +27 -0
  52. data/lib/csv_plus_plus/reader/rubyxl.rb +37 -0
  53. data/lib/csv_plus_plus/reader.rb +14 -0
  54. data/lib/csv_plus_plus/runtime/graph.rb +6 -6
  55. data/lib/csv_plus_plus/runtime/{position_tracker.rb → position.rb} +16 -5
  56. data/lib/csv_plus_plus/runtime/references.rb +32 -27
  57. data/lib/csv_plus_plus/runtime/runtime.rb +73 -67
  58. data/lib/csv_plus_plus/runtime/scope.rb +280 -0
  59. data/lib/csv_plus_plus/runtime.rb +9 -9
  60. data/lib/csv_plus_plus/source_code.rb +14 -9
  61. data/lib/csv_plus_plus/template.rb +17 -12
  62. data/lib/csv_plus_plus/version.rb +1 -1
  63. data/lib/csv_plus_plus/writer/csv.rb +32 -5
  64. data/lib/csv_plus_plus/writer/excel.rb +19 -6
  65. data/lib/csv_plus_plus/writer/file_backer_upper.rb +27 -14
  66. data/lib/csv_plus_plus/writer/google_sheets.rb +23 -129
  67. data/lib/csv_plus_plus/writer/{google_sheet_builder.rb → google_sheets_builder.rb} +39 -55
  68. data/lib/csv_plus_plus/writer/merger.rb +31 -0
  69. data/lib/csv_plus_plus/writer/open_document.rb +16 -2
  70. data/lib/csv_plus_plus/writer/rubyxl_builder.rb +68 -43
  71. data/lib/csv_plus_plus/writer/writer.rb +42 -0
  72. data/lib/csv_plus_plus/writer.rb +58 -19
  73. data/lib/csv_plus_plus.rb +26 -14
  74. metadata +37 -12
  75. data/lib/csv_plus_plus/entities/cell_reference.rb +0 -231
  76. data/lib/csv_plus_plus/entities/variable.rb +0 -37
  77. data/lib/csv_plus_plus/error/syntax_error.rb +0 -71
  78. data/lib/csv_plus_plus/google_options.rb +0 -32
  79. data/lib/csv_plus_plus/lexer/lexer.rb +0 -89
  80. data/lib/csv_plus_plus/runtime/can_define_references.rb +0 -87
  81. data/lib/csv_plus_plus/runtime/can_resolve_references.rb +0 -209
  82. data/lib/csv_plus_plus/writer/base_writer.rb +0 -45
@@ -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.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-04-11 00:00:00.000000000 Z
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/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,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/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
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