csv_plus_plus 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +18 -62
- data/lib/csv_plus_plus/benchmarked_compiler.rb +62 -0
- data/lib/csv_plus_plus/{code_section.rb → can_define_references.rb} +22 -35
- data/lib/csv_plus_plus/can_resolve_references.rb +8 -0
- data/lib/csv_plus_plus/cell.rb +3 -3
- data/lib/csv_plus_plus/cli.rb +24 -7
- data/lib/csv_plus_plus/color.rb +12 -6
- data/lib/csv_plus_plus/compiler.rb +156 -0
- data/lib/csv_plus_plus/data_validation.rb +138 -0
- data/lib/csv_plus_plus/{language → entities}/ast_builder.rb +3 -5
- data/lib/csv_plus_plus/entities/boolean.rb +31 -0
- data/lib/csv_plus_plus/{language → entities}/builtins.rb +2 -4
- data/lib/csv_plus_plus/entities/cell_reference.rb +60 -0
- data/lib/csv_plus_plus/entities/date.rb +30 -0
- data/lib/csv_plus_plus/entities/entity.rb +84 -0
- data/lib/csv_plus_plus/entities/function.rb +33 -0
- data/lib/csv_plus_plus/entities/function_call.rb +35 -0
- data/lib/csv_plus_plus/entities/number.rb +34 -0
- data/lib/csv_plus_plus/entities/runtime_value.rb +26 -0
- data/lib/csv_plus_plus/entities/string.rb +29 -0
- data/lib/csv_plus_plus/entities/variable.rb +25 -0
- data/lib/csv_plus_plus/entities.rb +33 -0
- data/lib/csv_plus_plus/error/error.rb +10 -0
- data/lib/csv_plus_plus/error/formula_syntax_error.rb +36 -0
- data/lib/csv_plus_plus/error/modifier_syntax_error.rb +27 -0
- data/lib/csv_plus_plus/error/modifier_validation_error.rb +49 -0
- data/lib/csv_plus_plus/{language → error}/syntax_error.rb +6 -14
- data/lib/csv_plus_plus/error/writer_error.rb +9 -0
- data/lib/csv_plus_plus/error.rb +9 -2
- data/lib/csv_plus_plus/expand.rb +3 -1
- data/lib/csv_plus_plus/google_api_client.rb +4 -0
- data/lib/csv_plus_plus/lexer/lexer.rb +13 -6
- data/lib/csv_plus_plus/modifier/conditional_formatting.rb +17 -0
- data/lib/csv_plus_plus/modifier.rb +73 -65
- data/lib/csv_plus_plus/{language → parser}/cell_value.tab.rb +20 -20
- data/lib/csv_plus_plus/{language → parser}/code_section.tab.rb +83 -87
- data/lib/csv_plus_plus/parser/modifier.tab.rb +484 -0
- data/lib/csv_plus_plus/references.rb +68 -0
- data/lib/csv_plus_plus/row.rb +0 -3
- data/lib/csv_plus_plus/runtime.rb +199 -0
- data/lib/csv_plus_plus/scope.rb +196 -0
- data/lib/csv_plus_plus/template.rb +10 -10
- data/lib/csv_plus_plus/validated_modifier.rb +164 -0
- data/lib/csv_plus_plus/version.rb +1 -1
- data/lib/csv_plus_plus/writer/file_backer_upper.rb +6 -4
- data/lib/csv_plus_plus/writer/google_sheet_builder.rb +24 -29
- data/lib/csv_plus_plus/writer/google_sheet_modifier.rb +33 -12
- data/lib/csv_plus_plus/writer/rubyxl_builder.rb +3 -6
- data/lib/csv_plus_plus.rb +19 -10
- metadata +34 -24
- data/lib/csv_plus_plus/language/benchmarked_compiler.rb +0 -65
- data/lib/csv_plus_plus/language/compiler.rb +0 -152
- data/lib/csv_plus_plus/language/entities/boolean.rb +0 -33
- data/lib/csv_plus_plus/language/entities/cell_reference.rb +0 -33
- data/lib/csv_plus_plus/language/entities/entity.rb +0 -86
- data/lib/csv_plus_plus/language/entities/function.rb +0 -35
- data/lib/csv_plus_plus/language/entities/function_call.rb +0 -37
- data/lib/csv_plus_plus/language/entities/number.rb +0 -36
- data/lib/csv_plus_plus/language/entities/runtime_value.rb +0 -28
- data/lib/csv_plus_plus/language/entities/string.rb +0 -31
- data/lib/csv_plus_plus/language/entities/variable.rb +0 -25
- data/lib/csv_plus_plus/language/entities.rb +0 -28
- data/lib/csv_plus_plus/language/references.rb +0 -70
- data/lib/csv_plus_plus/language/runtime.rb +0 -205
- data/lib/csv_plus_plus/language/scope.rb +0 -192
- 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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
98
|
+
::Google::Apis::SheetsV4::UpdateCellsRequest.new(
|
105
99
|
fields: '*',
|
106
|
-
start:
|
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
|
-
|
118
|
-
|
119
|
-
|
120
|
-
top: mod.border_along?(
|
121
|
-
right: mod.border_along?(
|
122
|
-
left: mod.border_along?(
|
123
|
-
bottom: mod.border_along?(
|
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
|
-
|
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
|
-
|
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
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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?(
|
36
|
-
italic: formatted?(
|
37
|
-
strikethrough: formatted?(
|
38
|
-
underline: formatted?(
|
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
|
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
|
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
|
-
|
15
|
-
require_relative 'csv_plus_plus/
|
16
|
-
require_relative 'csv_plus_plus/
|
17
|
-
|
18
|
-
require_relative 'csv_plus_plus/
|
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::
|
45
|
+
runtime = ::CSVPlusPlus::Runtime.new(input:, filename:)
|
37
46
|
|
38
|
-
::CSVPlusPlus::
|
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.
|
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
|
+
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.
|
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
|