csv_plus_plus 0.1.1 → 0.1.2

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/README.md +18 -62
  4. data/lib/csv_plus_plus/benchmarked_compiler.rb +62 -0
  5. data/lib/csv_plus_plus/{code_section.rb → can_define_references.rb} +22 -35
  6. data/lib/csv_plus_plus/can_resolve_references.rb +8 -0
  7. data/lib/csv_plus_plus/cell.rb +3 -3
  8. data/lib/csv_plus_plus/cli.rb +24 -7
  9. data/lib/csv_plus_plus/color.rb +12 -6
  10. data/lib/csv_plus_plus/compiler.rb +156 -0
  11. data/lib/csv_plus_plus/data_validation.rb +138 -0
  12. data/lib/csv_plus_plus/{language → entities}/ast_builder.rb +3 -5
  13. data/lib/csv_plus_plus/entities/boolean.rb +31 -0
  14. data/lib/csv_plus_plus/{language → entities}/builtins.rb +2 -4
  15. data/lib/csv_plus_plus/entities/cell_reference.rb +60 -0
  16. data/lib/csv_plus_plus/entities/date.rb +30 -0
  17. data/lib/csv_plus_plus/entities/entity.rb +84 -0
  18. data/lib/csv_plus_plus/entities/function.rb +33 -0
  19. data/lib/csv_plus_plus/entities/function_call.rb +35 -0
  20. data/lib/csv_plus_plus/entities/number.rb +34 -0
  21. data/lib/csv_plus_plus/entities/runtime_value.rb +26 -0
  22. data/lib/csv_plus_plus/entities/string.rb +29 -0
  23. data/lib/csv_plus_plus/entities/variable.rb +25 -0
  24. data/lib/csv_plus_plus/entities.rb +33 -0
  25. data/lib/csv_plus_plus/error/error.rb +10 -0
  26. data/lib/csv_plus_plus/error/formula_syntax_error.rb +36 -0
  27. data/lib/csv_plus_plus/error/modifier_syntax_error.rb +27 -0
  28. data/lib/csv_plus_plus/error/modifier_validation_error.rb +49 -0
  29. data/lib/csv_plus_plus/{language → error}/syntax_error.rb +6 -14
  30. data/lib/csv_plus_plus/error/writer_error.rb +9 -0
  31. data/lib/csv_plus_plus/error.rb +9 -2
  32. data/lib/csv_plus_plus/expand.rb +3 -1
  33. data/lib/csv_plus_plus/google_api_client.rb +4 -0
  34. data/lib/csv_plus_plus/lexer/lexer.rb +13 -6
  35. data/lib/csv_plus_plus/modifier/conditional_formatting.rb +17 -0
  36. data/lib/csv_plus_plus/modifier.rb +73 -65
  37. data/lib/csv_plus_plus/{language → parser}/cell_value.tab.rb +20 -20
  38. data/lib/csv_plus_plus/{language → parser}/code_section.tab.rb +83 -87
  39. data/lib/csv_plus_plus/parser/modifier.tab.rb +484 -0
  40. data/lib/csv_plus_plus/references.rb +68 -0
  41. data/lib/csv_plus_plus/row.rb +0 -3
  42. data/lib/csv_plus_plus/runtime.rb +199 -0
  43. data/lib/csv_plus_plus/scope.rb +196 -0
  44. data/lib/csv_plus_plus/template.rb +10 -10
  45. data/lib/csv_plus_plus/validated_modifier.rb +164 -0
  46. data/lib/csv_plus_plus/version.rb +1 -1
  47. data/lib/csv_plus_plus/writer/file_backer_upper.rb +6 -4
  48. data/lib/csv_plus_plus/writer/google_sheet_builder.rb +24 -29
  49. data/lib/csv_plus_plus/writer/google_sheet_modifier.rb +33 -12
  50. data/lib/csv_plus_plus/writer/rubyxl_builder.rb +3 -6
  51. data/lib/csv_plus_plus.rb +19 -10
  52. metadata +34 -24
  53. data/lib/csv_plus_plus/language/benchmarked_compiler.rb +0 -65
  54. data/lib/csv_plus_plus/language/compiler.rb +0 -152
  55. data/lib/csv_plus_plus/language/entities/boolean.rb +0 -33
  56. data/lib/csv_plus_plus/language/entities/cell_reference.rb +0 -33
  57. data/lib/csv_plus_plus/language/entities/entity.rb +0 -86
  58. data/lib/csv_plus_plus/language/entities/function.rb +0 -35
  59. data/lib/csv_plus_plus/language/entities/function_call.rb +0 -37
  60. data/lib/csv_plus_plus/language/entities/number.rb +0 -36
  61. data/lib/csv_plus_plus/language/entities/runtime_value.rb +0 -28
  62. data/lib/csv_plus_plus/language/entities/string.rb +0 -31
  63. data/lib/csv_plus_plus/language/entities/variable.rb +0 -25
  64. data/lib/csv_plus_plus/language/entities.rb +0 -28
  65. data/lib/csv_plus_plus/language/references.rb +0 -70
  66. data/lib/csv_plus_plus/language/runtime.rb +0 -205
  67. data/lib/csv_plus_plus/language/scope.rb +0 -192
  68. data/lib/csv_plus_plus/modifier.tab.rb +0 -907
@@ -25,12 +25,6 @@ module CSVPlusPlus
25
25
 
26
26
  private
27
27
 
28
- def sheets_ns
29
- ::Google::Apis::SheetsV4
30
- end
31
-
32
- def sheets_color(color); end
33
-
34
28
  def set_extended_value_type!(extended_value, value)
35
29
  v = value || ''
36
30
  if v.start_with?('=')
@@ -45,7 +39,7 @@ module CSVPlusPlus
45
39
  end
46
40
 
47
41
  def build_cell_format(mod)
48
- sheets_ns::CellFormat.new.tap do |cf|
42
+ ::Google::Apis::SheetsV4::CellFormat.new.tap do |cf|
49
43
  cf.text_format = mod.text_format
50
44
 
51
45
  cf.horizontal_alignment = mod.halign
@@ -56,7 +50,7 @@ module CSVPlusPlus
56
50
  end
57
51
 
58
52
  def grid_range_for_cell(cell)
59
- sheets_ns::GridRange.new(
53
+ ::Google::Apis::SheetsV4::GridRange.new(
60
54
  sheet_id: @sheet_id,
61
55
  start_column_index: cell.index,
62
56
  end_column_index: cell.index + 1,
@@ -72,7 +66,7 @@ module CSVPlusPlus
72
66
  end
73
67
 
74
68
  def build_cell_value(cell)
75
- sheets_ns::ExtendedValue.new.tap do |xv|
69
+ ::Google::Apis::SheetsV4::ExtendedValue.new.tap do |xv|
76
70
  value =
77
71
  if cell.value.nil?
78
72
  current_value(cell.row_index, cell.index)
@@ -87,7 +81,7 @@ module CSVPlusPlus
87
81
  def build_cell_data(cell)
88
82
  mod = ::CSVPlusPlus::Writer::GoogleSheetModifier.new(cell.modifier)
89
83
 
90
- sheets_ns::CellData.new.tap do |cd|
84
+ ::Google::Apis::SheetsV4::CellData.new.tap do |cd|
91
85
  cd.user_entered_format = build_cell_format(mod)
92
86
  cd.note = mod.note if mod.note
93
87
 
@@ -97,13 +91,13 @@ module CSVPlusPlus
97
91
  end
98
92
 
99
93
  def build_row_data(row)
100
- sheets_ns::RowData.new(values: row.cells.map { |cell| build_cell_data(cell) })
94
+ ::Google::Apis::SheetsV4::RowData.new(values: row.cells.map { |cell| build_cell_data(cell) })
101
95
  end
102
96
 
103
97
  def build_update_cells_request(rows)
104
- sheets_ns::UpdateCellsRequest.new(
98
+ ::Google::Apis::SheetsV4::UpdateCellsRequest.new(
105
99
  fields: '*',
106
- start: sheets_ns::GridCoordinate.new(
100
+ start: ::Google::Apis::SheetsV4::GridCoordinate.new(
107
101
  sheet_id: @sheet_id,
108
102
  column_index: @column_index,
109
103
  row_index: @row_index
@@ -113,29 +107,31 @@ module CSVPlusPlus
113
107
  end
114
108
 
115
109
  def build_border(cell)
116
- mod = cell.modifier
117
- # TODO: allow different border styles per side
118
- border = sheets_ns::Border.new(color: mod.bordercolor || '#000000', style: mod.borderstyle || 'solid')
119
- sheets_ns::UpdateBordersRequest.new(
120
- top: mod.border_along?('top') ? border : nil,
121
- right: mod.border_along?('right') ? border : nil,
122
- left: mod.border_along?('left') ? border : nil,
123
- bottom: mod.border_along?('bottom') ? border : nil,
110
+ mod = ::CSVPlusPlus::Writer::GoogleSheetModifier.new(cell.modifier)
111
+ border = mod.border
112
+
113
+ ::Google::Apis::SheetsV4::UpdateBordersRequest.new(
114
+ top: mod.border_along?(:top) ? border : nil,
115
+ right: mod.border_along?(:right) ? border : nil,
116
+ left: mod.border_along?(:left) ? border : nil,
117
+ bottom: mod.border_along?(:bottom) ? border : nil,
124
118
  range: grid_range_for_cell(cell)
125
119
  )
126
120
  end
127
121
 
128
122
  def build_update_borders_request(cell)
129
- sheets_ns::Request.new(update_borders: build_border(cell))
123
+ ::Google::Apis::SheetsV4::Request.new(update_borders: build_border(cell))
124
+ end
125
+
126
+ def chunked_requests(rows)
127
+ rows.each_slice(1000).to_a.map do |chunked_rows|
128
+ ::Google::Apis::SheetsV4::Request.new(update_cells: build_update_cells_request(chunked_rows))
129
+ end
130
130
  end
131
131
 
132
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
133
132
  def build_batch_request(rows)
134
- sheets_ns::BatchUpdateSpreadsheetRequest.new.tap do |bu|
135
- bu.requests =
136
- rows.each_slice(1000).to_a.map do |chunked_rows|
137
- sheets_ns::Request.new(update_cells: build_update_cells_request(chunked_rows))
138
- end
133
+ ::Google::Apis::SheetsV4::BatchUpdateSpreadsheetRequest.new.tap do |bu|
134
+ bu.requests = chunked_requests(rows)
139
135
 
140
136
  rows.each do |row|
141
137
  row.cells.filter { |c| c.modifier.any_border? }
@@ -145,7 +141,6 @@ module CSVPlusPlus
145
141
  end
146
142
  end
147
143
  end
148
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
149
144
  end
150
145
  # rubocop:enable Metrics/ClassLength
151
146
  end
@@ -4,44 +4,65 @@ module CSVPlusPlus
4
4
  module Writer
5
5
  # Decorate a Modifier so it can be written to the Google Sheets API
6
6
  class GoogleSheetModifier < ::SimpleDelegator
7
- # Format the halign for Google Sheets
8
- def halign
9
- super&.upcase
10
- end
11
-
12
- # Format the valign for Google Sheets
13
- def valign
14
- super&.upcase
7
+ # Format the border for Google Sheets
8
+ #
9
+ # @return [Google::Apis::SheetsV4::Border]
10
+ def border
11
+ # TODO: allow different border styles per side
12
+ ::Google::Apis::SheetsV4::Border.new(
13
+ color: bordercolor&.to_s || '#000000',
14
+ style: borderstyle&.to_s || 'solid'
15
+ )
15
16
  end
16
17
 
17
18
  # Format the color for Google Sheets
19
+ #
20
+ # @return [Google::Apis::SheetsV4::Color]
18
21
  def color
19
22
  google_sheets_color(super) if super
20
23
  end
21
24
 
22
25
  # Format the fontcolor for Google Sheets
26
+ #
27
+ # @return [Google::Apis::SheetsV4::Color]
23
28
  def fontcolor
24
29
  google_sheets_color(super) if super
25
30
  end
26
31
 
32
+ # Format the halign for Google Sheets
33
+ #
34
+ # @return [String]
35
+ def halign
36
+ super&.to_s&.upcase
37
+ end
38
+
27
39
  # Format the numberformat for Google Sheets
40
+ #
41
+ # @return [::Google::Apis::SheetsV4::NumberFormat]
28
42
  def numberformat
29
43
  ::Google::Apis::SheetsV4::NumberFormat.new(type: super) if super
30
44
  end
31
45
 
32
46
  # Builds a SheetsV4::TextFormat with the underlying Modifier
47
+ #
48
+ # @return [::Google::Apis::SheetsV4::TextFormat]
33
49
  def text_format
34
50
  ::Google::Apis::SheetsV4::TextFormat.new(
35
- bold: formatted?('bold') || nil,
36
- italic: formatted?('italic') || nil,
37
- strikethrough: formatted?('strikethrough') || nil,
38
- underline: formatted?('underline') || nil,
51
+ bold: formatted?(:bold) || nil,
52
+ italic: formatted?(:italic) || nil,
53
+ strikethrough: formatted?(:strikethrough) || nil,
54
+ underline: formatted?(:underline) || nil,
39
55
  font_family: fontfamily,
40
56
  font_size: fontsize,
41
57
  foreground_color: fontcolor
42
58
  )
43
59
  end
44
60
 
61
+ # Format the valign for Google Sheets
62
+ def valign
63
+ super&.to_s&.upcase
64
+ end
65
+
45
66
  private
46
67
 
47
68
  def google_sheets_color(color)
@@ -24,10 +24,7 @@ module CSVPlusPlus
24
24
  #
25
25
  # @return [RubyXL::Workbook]
26
26
  def build_workbook
27
- open_workbook.tap do |workbook|
28
- @worksheet = workbook[@sheet_name]
29
- build_workbook!
30
- end
27
+ open_workbook.tap { build_workbook! }
31
28
  end
32
29
 
33
30
  private
@@ -107,11 +104,11 @@ module CSVPlusPlus
107
104
  def open_workbook
108
105
  if ::File.exist?(@input_filename)
109
106
  ::RubyXL::Parser.parse(@input_filename).tap do |workbook|
110
- workbook.add_worksheet(@sheet_name) unless workbook[@sheet_name]
107
+ @worksheet = workbook[@sheet_name] || workbook.add_worksheet(@sheet_name)
111
108
  end
112
109
  else
113
110
  ::RubyXL::Workbook.new.tap do |workbook|
114
- workbook.worksheets[0].sheet_name = @sheet_name
111
+ @worksheet = workbook.worksheets[0].tap { |w| w.sheet_name = @sheet_name }
115
112
  end
116
113
  end
117
114
  end
data/lib/csv_plus_plus.rb CHANGED
@@ -1,26 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'benchmark'
4
+ require 'csv'
5
+ require 'fileutils'
3
6
  require 'google/apis/drive_v3'
4
7
  require 'google/apis/sheets_v4'
5
8
  require 'googleauth'
9
+ require 'pathname'
6
10
  require 'rubyXL'
7
11
  require 'rubyXL/convenience_methods'
8
12
  require 'set'
13
+ require 'tempfile'
14
+
15
+ require_relative 'csv_plus_plus/entities'
16
+ require_relative 'csv_plus_plus/error'
9
17
 
10
18
  require_relative 'csv_plus_plus/cell'
11
19
  require_relative 'csv_plus_plus/cli'
12
- require_relative 'csv_plus_plus/code_section'
13
20
  require_relative 'csv_plus_plus/color'
14
- require_relative 'csv_plus_plus/error'
15
- require_relative 'csv_plus_plus/language/builtins'
16
- require_relative 'csv_plus_plus/language/compiler'
17
- require_relative 'csv_plus_plus/language/runtime'
18
- require_relative 'csv_plus_plus/language/syntax_error'
21
+
22
+ require_relative 'csv_plus_plus/compiler'
23
+ require_relative 'csv_plus_plus/runtime'
24
+
25
+ require_relative 'csv_plus_plus/lexer'
26
+ require_relative 'csv_plus_plus/lexer/tokenizer'
19
27
  require_relative 'csv_plus_plus/modifier'
20
- require_relative 'csv_plus_plus/modifier.tab'
21
28
  require_relative 'csv_plus_plus/options'
29
+ require_relative 'csv_plus_plus/parser/modifier.tab'
22
30
  require_relative 'csv_plus_plus/row'
23
31
  require_relative 'csv_plus_plus/template'
32
+ require_relative 'csv_plus_plus/validated_modifier'
24
33
  require_relative 'csv_plus_plus/writer'
25
34
 
26
35
  # A programming language for writing rich CSV files
@@ -33,9 +42,9 @@ module CSVPlusPlus
33
42
  def self.apply_template_to_sheet!(input, filename, options)
34
43
  warn(options.verbose_summary) if options.verbose
35
44
 
36
- runtime = ::CSVPlusPlus::Language::Runtime.new(input:, filename:)
45
+ runtime = ::CSVPlusPlus::Runtime.new(input:, filename:)
37
46
 
38
- ::CSVPlusPlus::Language::Compiler.with_compiler(options:, runtime:) do |compiler|
47
+ ::CSVPlusPlus::Compiler.with_compiler(options:, runtime:) do |compiler|
39
48
  template = compiler.compile_template
40
49
 
41
50
  warn(template.verbose_summary) if options.verbose
@@ -50,8 +59,8 @@ module CSVPlusPlus
50
59
  # @param compiler [Compiler] The compiler currently in use
51
60
  # @param options [Options] The options we're running with
52
61
  def self.write_template(template, compiler, options)
53
- output = ::CSVPlusPlus::Writer.writer(options)
54
62
  compiler.outputting! do
63
+ output = ::CSVPlusPlus::Writer.writer(options)
55
64
  output.write_backup if options.backup
56
65
  output.write(template)
57
66
  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.1
4
+ version: 0.1.2
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-03-11 00:00:00.000000000 Z
11
+ date: 2023-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: google-apis-drive_v3
@@ -123,44 +123,54 @@ files:
123
123
  - bin/csv++
124
124
  - bin/csvpp
125
125
  - lib/csv_plus_plus.rb
126
+ - lib/csv_plus_plus/benchmarked_compiler.rb
127
+ - lib/csv_plus_plus/can_define_references.rb
128
+ - lib/csv_plus_plus/can_resolve_references.rb
126
129
  - lib/csv_plus_plus/cell.rb
127
130
  - lib/csv_plus_plus/cli.rb
128
131
  - lib/csv_plus_plus/cli_flag.rb
129
- - lib/csv_plus_plus/code_section.rb
130
132
  - lib/csv_plus_plus/color.rb
133
+ - lib/csv_plus_plus/compiler.rb
134
+ - lib/csv_plus_plus/data_validation.rb
135
+ - lib/csv_plus_plus/entities.rb
136
+ - lib/csv_plus_plus/entities/ast_builder.rb
137
+ - lib/csv_plus_plus/entities/boolean.rb
138
+ - lib/csv_plus_plus/entities/builtins.rb
139
+ - lib/csv_plus_plus/entities/cell_reference.rb
140
+ - lib/csv_plus_plus/entities/date.rb
141
+ - lib/csv_plus_plus/entities/entity.rb
142
+ - lib/csv_plus_plus/entities/function.rb
143
+ - lib/csv_plus_plus/entities/function_call.rb
144
+ - lib/csv_plus_plus/entities/number.rb
145
+ - lib/csv_plus_plus/entities/runtime_value.rb
146
+ - lib/csv_plus_plus/entities/string.rb
147
+ - lib/csv_plus_plus/entities/variable.rb
131
148
  - lib/csv_plus_plus/error.rb
149
+ - lib/csv_plus_plus/error/error.rb
150
+ - lib/csv_plus_plus/error/formula_syntax_error.rb
151
+ - lib/csv_plus_plus/error/modifier_syntax_error.rb
152
+ - lib/csv_plus_plus/error/modifier_validation_error.rb
153
+ - lib/csv_plus_plus/error/syntax_error.rb
154
+ - lib/csv_plus_plus/error/writer_error.rb
132
155
  - lib/csv_plus_plus/expand.rb
133
156
  - lib/csv_plus_plus/google_api_client.rb
134
157
  - lib/csv_plus_plus/google_options.rb
135
158
  - lib/csv_plus_plus/graph.rb
136
- - lib/csv_plus_plus/language/ast_builder.rb
137
- - lib/csv_plus_plus/language/benchmarked_compiler.rb
138
- - lib/csv_plus_plus/language/builtins.rb
139
- - lib/csv_plus_plus/language/cell_value.tab.rb
140
- - lib/csv_plus_plus/language/code_section.tab.rb
141
- - lib/csv_plus_plus/language/compiler.rb
142
- - lib/csv_plus_plus/language/entities.rb
143
- - lib/csv_plus_plus/language/entities/boolean.rb
144
- - lib/csv_plus_plus/language/entities/cell_reference.rb
145
- - lib/csv_plus_plus/language/entities/entity.rb
146
- - lib/csv_plus_plus/language/entities/function.rb
147
- - lib/csv_plus_plus/language/entities/function_call.rb
148
- - lib/csv_plus_plus/language/entities/number.rb
149
- - lib/csv_plus_plus/language/entities/runtime_value.rb
150
- - lib/csv_plus_plus/language/entities/string.rb
151
- - lib/csv_plus_plus/language/entities/variable.rb
152
- - lib/csv_plus_plus/language/references.rb
153
- - lib/csv_plus_plus/language/runtime.rb
154
- - lib/csv_plus_plus/language/scope.rb
155
- - lib/csv_plus_plus/language/syntax_error.rb
156
159
  - lib/csv_plus_plus/lexer.rb
157
160
  - lib/csv_plus_plus/lexer/lexer.rb
158
161
  - lib/csv_plus_plus/lexer/tokenizer.rb
159
162
  - lib/csv_plus_plus/modifier.rb
160
- - lib/csv_plus_plus/modifier.tab.rb
163
+ - lib/csv_plus_plus/modifier/conditional_formatting.rb
161
164
  - lib/csv_plus_plus/options.rb
165
+ - lib/csv_plus_plus/parser/cell_value.tab.rb
166
+ - lib/csv_plus_plus/parser/code_section.tab.rb
167
+ - lib/csv_plus_plus/parser/modifier.tab.rb
168
+ - lib/csv_plus_plus/references.rb
162
169
  - lib/csv_plus_plus/row.rb
170
+ - lib/csv_plus_plus/runtime.rb
171
+ - lib/csv_plus_plus/scope.rb
163
172
  - lib/csv_plus_plus/template.rb
173
+ - lib/csv_plus_plus/validated_modifier.rb
164
174
  - lib/csv_plus_plus/version.rb
165
175
  - lib/csv_plus_plus/writer.rb
166
176
  - lib/csv_plus_plus/writer/base_writer.rb
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'benchmark'
4
-
5
- module CSVPlusPlus
6
- module Language
7
- # Extend a +Compiler+ class and add benchmark timings
8
- # @attr_reader timings [Array<Benchmark::Tms>] +Benchmark+ timings that have been accumulated by each step of
9
- # compilation
10
- # @attr_reader benchmark [Benchmark] A +Benchmark+ instance
11
- module BenchmarkedCompiler
12
- attr_reader :benchmark, :timings
13
-
14
- # Wrap a +Compiler+ with our instance methods that add benchmarks
15
- def self.with_benchmarks(compiler, &block)
16
- ::Benchmark.benchmark(::Benchmark::CAPTION, 25, ::Benchmark::FORMAT, '> Total') do |x|
17
- # compiler = new(options:, runtime:, benchmark: x)
18
- compiler.extend(self)
19
- compiler.benchmark = x
20
-
21
- block.call(compiler)
22
-
23
- [compiler.timings.reduce(:+)]
24
- end
25
- end
26
-
27
- # @param benchmark [Benchmark] A +Benchmark+ instance
28
- def benchmark=(benchmark)
29
- @benchmark = benchmark
30
- @timings = []
31
- end
32
-
33
- # Time the Compiler#outputting! stage
34
- def outputting!
35
- time_stage('Writing the spreadsheet') { super }
36
- end
37
-
38
- protected
39
-
40
- def parse_code_section!
41
- time_stage('Parsing code section') { super }
42
- end
43
-
44
- def parse_csv_section!
45
- time_stage('Parsing CSV section') { super }
46
- end
47
-
48
- def expanding
49
- time_stage('Expanding rows') { super }
50
- end
51
-
52
- def resolve_all_cells!(template)
53
- time_stage('Resolving each cell') { super(template) }
54
- end
55
-
56
- private
57
-
58
- def time_stage(stage, &block)
59
- ret = nil
60
- @timings << @benchmark.report(stage) { ret = block.call }
61
- ret
62
- end
63
- end
64
- end
65
- end
@@ -1,152 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'csv'
4
-
5
- require_relative 'benchmarked_compiler'
6
- require_relative 'code_section.tab'
7
- require_relative 'entities'
8
- require_relative 'runtime'
9
- require_relative 'scope'
10
-
11
- module CSVPlusPlus
12
- module Language
13
- # Encapsulates the parsing and building of objects (+Template+ -> +Row+ -> +Cell+). Variable resolution is delegated
14
- # to the +Scope+
15
- #
16
- # @attr_reader options [Options] The +Options+ to compile with
17
- # @attr_reader runtime [Runtime] The runtime execution
18
- # @attr_reader scope [Scope] +Scope+ for variable resolution
19
- class Compiler
20
- attr_reader :timings, :benchmark, :options, :runtime, :scope
21
-
22
- # Create a compiler and make sure it gets cleaned up
23
- #
24
- # @param runtime [Runtime] The initial +Runtime+ for the compiler
25
- # @param options [Options]
26
- def self.with_compiler(runtime:, options:, &block)
27
- compiler = new(options:, runtime:)
28
- if options.verbose
29
- ::CSVPlusPlus::Language::BenchmarkedCompiler.with_benchmarks(compiler) do |c|
30
- block.call(c)
31
- end
32
- else
33
- yield(compiler)
34
- end
35
- ensure
36
- runtime.cleanup!
37
- end
38
-
39
- # @param runtime [Runtime]
40
- # @param options [Options]
41
- # @param scope [Scope, nil]
42
- def initialize(runtime:, options:, scope: nil)
43
- @options = options
44
- @runtime = runtime
45
- @scope = scope || ::CSVPlusPlus::Language::Scope.new(runtime:)
46
- end
47
-
48
- # Write the compiled results
49
- def outputting!
50
- @runtime.start_at_csv!
51
- yield
52
- end
53
-
54
- # Compile a template and return a +::CSVPlusPlus::Template+ instance ready to be written with a +Writer+
55
- #
56
- # @return [Template]
57
- def compile_template
58
- parse_code_section!
59
- rows = parse_csv_section!
60
-
61
- ::CSVPlusPlus::Template.new(rows:, code_section: scope.code_section).tap do |t|
62
- t.validate_infinite_expands(@runtime)
63
- expanding { t.expand_rows! }
64
- resolve_all_cells!(t)
65
- end
66
- end
67
-
68
- # @return [String]
69
- def to_s
70
- "Compiler(options: #{@options}, runtime: #{@runtime}, scope: #{@scope})"
71
- end
72
-
73
- protected
74
-
75
- # Parses the input file and returns a +CodeSection+
76
- #
77
- # @return [CodeSection]
78
- def parse_code_section!
79
- @runtime.start!
80
- parsing_code_section do |input|
81
- code_section, csv_section = ::CSVPlusPlus::Language::CodeSectionParser.new.parse(input, @runtime)
82
- # TODO: infer a type
83
- # allow user-supplied key/values to override anything global or from the code section
84
- code_section.def_variables(
85
- options.key_values.transform_values { |v| ::CSVPlusPlus::Language::Entities::String.new(v.to_s) }
86
- )
87
- @scope.code_section = code_section
88
-
89
- # return the csv_section to the caller because they're gonna re-write input with it
90
- next csv_section
91
- end
92
- @scope.code_section
93
- end
94
-
95
- # Parse the CSV section and return an array of +Row+s
96
- #
97
- # @return [Array<Row>]
98
- def parse_csv_section!
99
- @runtime.start_at_csv!
100
- @runtime.map_rows(::CSV.new(runtime.input)) do |csv_row|
101
- parse_row(csv_row)
102
- end
103
- ensure
104
- # we're done with the file and everything is in memory
105
- @runtime.cleanup!
106
- end
107
-
108
- # Iterates through each cell of each row and resolves it's variable and function references.
109
- #
110
- # @param template [Template]
111
- # @return [Array<Entity>]
112
- def resolve_all_cells!(template)
113
- @runtime.start_at_csv!
114
- @runtime.map_rows(template.rows, cells_too: true) do |cell|
115
- cell.ast = @scope.resolve_cell_value if cell.ast
116
- end
117
- end
118
-
119
- # Expanding rows
120
- def expanding
121
- @runtime.start_at_csv!
122
- yield
123
- end
124
-
125
- private
126
-
127
- def parsing_code_section
128
- csv_section = yield(@runtime.input.read)
129
- @runtime.rewrite_input!(csv_section)
130
- end
131
-
132
- # Using the current +@runtime+ and the given +csv_row+ parse it into a +Row+ of +Cell+s
133
- # +csv_row+ should have already been run through a CSV parser and is an array of strings
134
- #
135
- # @param csv_row [Array<Array<String>>]
136
- # @return [Row]
137
- def parse_row(csv_row)
138
- row_modifier = ::CSVPlusPlus::Modifier.new(row_level: true)
139
-
140
- cells =
141
- @runtime.map_row(csv_row) do |value, _cell_index|
142
- cell_modifier = ::CSVPlusPlus::Modifier.new
143
- parsed_value = ::CSVPlusPlus::ModifierParser.new(row_modifier:, cell_modifier:).parse(value, @runtime)
144
-
145
- ::CSVPlusPlus::Cell.parse(parsed_value, runtime:, modifier: cell_modifier)
146
- end
147
-
148
- ::CSVPlusPlus::Row.new(@runtime.row_index, cells, row_modifier)
149
- end
150
- end
151
- end
152
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative './entity'
4
-
5
- module CSVPlusPlus
6
- module Language
7
- module Entities
8
- # A boolean value
9
- #
10
- # @attr_reader value [true, false]
11
- class Boolean < Entity
12
- attr_reader :value
13
-
14
- # @param value [String, Boolean]
15
- def initialize(value)
16
- super(:boolean)
17
- # TODO: probably can do a lot better in general on type validation
18
- @value = value.is_a?(::String) ? (value.downcase == 'true') : value
19
- end
20
-
21
- # @return [String]
22
- def to_s
23
- @value.to_s.upcase
24
- end
25
-
26
- # @return [boolean]
27
- def ==(other)
28
- super && value == other.value
29
- end
30
- end
31
- end
32
- end
33
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative './entity'
4
-
5
- module CSVPlusPlus
6
- module Language
7
- module Entities
8
- # A reference to a cell
9
- #
10
- # @attr_reader cell_reference [String] The cell reference in A1 format
11
- class CellReference < Entity
12
- attr_reader :cell_reference
13
-
14
- # @param cell_reference [String] The cell reference in A1 format
15
- def initialize(cell_reference)
16
- super(:cell_reference)
17
-
18
- @cell_reference = cell_reference
19
- end
20
-
21
- # @return [String]
22
- def to_s
23
- @cell_reference
24
- end
25
-
26
- # @return [Boolean]
27
- def ==(other)
28
- super && @cell_reference == other.cell_reference
29
- end
30
- end
31
- end
32
- end
33
- end