csv_plus_plus 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +1 -0
  4. data/lib/csv_plus_plus/cell.rb +24 -8
  5. data/lib/csv_plus_plus/cli.rb +29 -16
  6. data/lib/csv_plus_plus/cli_flag.rb +10 -2
  7. data/lib/csv_plus_plus/code_section.rb +22 -3
  8. data/lib/csv_plus_plus/color.rb +19 -5
  9. data/lib/csv_plus_plus/google_options.rb +6 -2
  10. data/lib/csv_plus_plus/graph.rb +0 -1
  11. data/lib/csv_plus_plus/language/ast_builder.rb +68 -0
  12. data/lib/csv_plus_plus/language/benchmarked_compiler.rb +65 -0
  13. data/lib/csv_plus_plus/language/builtins.rb +46 -0
  14. data/lib/csv_plus_plus/language/cell_value.tab.rb +1 -2
  15. data/lib/csv_plus_plus/language/code_section.tab.rb +1 -2
  16. data/lib/csv_plus_plus/language/compiler.rb +74 -86
  17. data/lib/csv_plus_plus/language/entities/boolean.rb +3 -2
  18. data/lib/csv_plus_plus/language/entities/cell_reference.rb +10 -3
  19. data/lib/csv_plus_plus/language/entities/entity.rb +20 -8
  20. data/lib/csv_plus_plus/language/entities/function.rb +6 -4
  21. data/lib/csv_plus_plus/language/entities/function_call.rb +4 -3
  22. data/lib/csv_plus_plus/language/entities/number.rb +6 -4
  23. data/lib/csv_plus_plus/language/entities/runtime_value.rb +9 -8
  24. data/lib/csv_plus_plus/language/entities/string.rb +6 -4
  25. data/lib/csv_plus_plus/language/references.rb +22 -5
  26. data/lib/csv_plus_plus/language/runtime.rb +80 -22
  27. data/lib/csv_plus_plus/language/scope.rb +29 -38
  28. data/lib/csv_plus_plus/language/syntax_error.rb +10 -5
  29. data/lib/csv_plus_plus/lexer/lexer.rb +25 -12
  30. data/lib/csv_plus_plus/lexer/tokenizer.rb +35 -11
  31. data/lib/csv_plus_plus/modifier.rb +38 -13
  32. data/lib/csv_plus_plus/modifier.tab.rb +2 -2
  33. data/lib/csv_plus_plus/options.rb +17 -2
  34. data/lib/csv_plus_plus/row.rb +15 -4
  35. data/lib/csv_plus_plus/template.rb +10 -6
  36. data/lib/csv_plus_plus/version.rb +1 -1
  37. data/lib/csv_plus_plus/writer/excel.rb +2 -9
  38. data/lib/csv_plus_plus/writer/file_backer_upper.rb +22 -20
  39. data/lib/csv_plus_plus/writer/google_sheet_builder.rb +8 -10
  40. data/lib/csv_plus_plus/writer/google_sheets.rb +4 -10
  41. data/lib/csv_plus_plus/writer/rubyxl_builder.rb +23 -15
  42. data/lib/csv_plus_plus/writer/rubyxl_modifier.rb +15 -8
  43. data/lib/csv_plus_plus.rb +21 -3
  44. metadata +5 -2
@@ -17,6 +17,7 @@ module_eval(<<'...end modifier.y/module_eval...', 'modifier.y', 121)
17
17
 
18
18
  include ::CSVPlusPlus::Lexer
19
19
 
20
+ # @param cell_modifier
20
21
  def initialize(cell_modifier:, row_modifier:)
21
22
  super()
22
23
 
@@ -42,11 +43,10 @@ module_eval(<<'...end modifier.y/module_eval...', 'modifier.y', 121)
42
43
  'modifier'
43
44
  end
44
45
 
45
- def tokenizer(input)
46
+ def tokenizer
46
47
  ::CSVPlusPlus::Lexer::Tokenizer.new(
47
48
  catchall: /\w+/,
48
49
  ignore: /\s+/,
49
- input:,
50
50
  stop_fn: lambda do |scanner|
51
51
  return false unless scanner.scan(/\]\]/)
52
52
 
@@ -4,7 +4,16 @@ require_relative './cli_flag'
4
4
  require_relative './google_options'
5
5
 
6
6
  module CSVPlusPlus
7
- # The options a user can supply
7
+ # The options a user can supply (via CLI flags)
8
+ #
9
+ # @attr backup [boolean] Create a backup of the spreadsheet before writing
10
+ # @attr create_if_not_exists [boolean] Create the spreadsheet if it does not exist?
11
+ # @attr key_values [Hash] Additional variables that can be supplied to the template
12
+ # @attr offset [Array<Integer>] An [x, y] offset (array with two integers)
13
+ # @attr output_filename [String] The file to write our compiled results to
14
+ # @attr sheet_name [String] The name of the spreadsheet to write to
15
+ # @attr verbose [boolean] Include extra verbose output?
16
+ # @attr_reader google [GoogleOptions] Options that are specific to the Google Sheets writer
8
17
  class Options
9
18
  attr_accessor :backup, :create_if_not_exists, :key_values, :offset, :output_filename, :sheet_name, :verbose
10
19
  attr_reader :google
@@ -19,11 +28,16 @@ module CSVPlusPlus
19
28
  end
20
29
 
21
30
  # Set the Google Sheet ID
31
+ #
32
+ # @param sheet_id [String] The identifier used by Google's API to reference the sheet. You can find it in the URL
33
+ # for the sheet
34
+ # @return [String]
22
35
  def google_sheet_id=(sheet_id)
23
36
  @google = ::CSVPlusPlus::GoogleOptions.new(sheet_id)
24
37
  end
25
38
 
26
39
  # Returns an error string or nil if there are no validation problems
40
+ # @return [String, nil]
27
41
  def validate
28
42
  return if @google || @output_filename
29
43
 
@@ -31,6 +45,7 @@ module CSVPlusPlus
31
45
  end
32
46
 
33
47
  # Return a string with a verbose description of what we're doing with the options
48
+ # @return [String]
34
49
  def verbose_summary
35
50
  <<~SUMMARY
36
51
  #{summary_divider}
@@ -55,7 +70,7 @@ module CSVPlusPlus
55
70
  SUMMARY
56
71
  end
57
72
 
58
- # to_s
73
+ # @return [String]
59
74
  def to_s
60
75
  "Options(create_if_not_exists: #{@create_if_not_exists}, google: #{@google}, key_values: #{@key_values}, " \
61
76
  "offset: #{@offset}, sheet_name: #{@sheet_name}, verbose: #{@verbose})"
@@ -4,37 +4,48 @@ require_relative 'cell'
4
4
  require_relative 'modifier.tab'
5
5
 
6
6
  module CSVPlusPlus
7
- ##
8
7
  # A row of a template
8
+ #
9
+ # @attr_reader cells [Array<Cell>]
10
+ # @attr_reader index [Integer] The index of this row
11
+ # @attr_reader modifier [Modifier] The modifier to apply to all cells in this row
9
12
  class Row
10
13
  attr_reader :cells, :index, :modifier
11
14
 
12
- # initialize
15
+ # @param index [Integer] The index of this row (starts at 0)
16
+ # @param cells [Array<Cell>] The cells belonging to this row
17
+ # @param modifier [Modifier] The modifier to apply to all cells in this row
13
18
  def initialize(index, cells, modifier)
14
19
  @cells = cells
15
20
  @modifier = modifier
16
21
  @index = index
17
22
  end
18
23
 
19
- # Set the row index. And update the index of all affected cells
24
+ # Set the row's +index+ and update the +row_index+ of all affected cells
25
+ #
26
+ # @param index [Integer] The index of this row (starts at 0)
20
27
  def index=(index)
21
28
  @index = index
22
29
  @cells.each { |cell| cell.row_index = index }
23
30
  end
24
31
 
25
32
  # How much this row will expand itself, if at all (0)
33
+ #
34
+ # @return [Integer]
26
35
  def expand_amount
27
36
  return 0 unless @modifier.expand
28
37
 
29
38
  @modifier.expand.repetitions || (1000 - @index)
30
39
  end
31
40
 
32
- # to_s
41
+ # @return [String]
33
42
  def to_s
34
43
  "Row(index: #{index}, modifier: #{modifier}, cells: #{cells})"
35
44
  end
36
45
 
37
46
  # Return a deep copy of this row
47
+ #
48
+ # @return [Row]
38
49
  def deep_clone
39
50
  ::Marshal.load(::Marshal.dump(self))
40
51
  end
@@ -2,21 +2,23 @@
2
2
 
3
3
  module CSVPlusPlus
4
4
  # Contains the flow and data from a code section and CSV section
5
+ #
6
+ # @attr_reader rows [Array<Row>] The +Row+s that comprise this +Template+
5
7
  class Template
6
- attr_reader :rows, :scope
8
+ attr_reader :rows
7
9
 
8
- # initialize
9
- def initialize(rows:, scope:)
10
+ # @param rows [Array<Row>] The +Row+s that comprise this +Template+
11
+ def initialize(rows:)
10
12
  @rows = rows
11
- @scope = scope
12
13
  end
13
14
 
14
- # to_s
15
+ # @return [String]
15
16
  def to_s
16
- "Template(rows: #{@rows}, scope: #{@scope})"
17
+ "Template(rows: #{@rows})"
17
18
  end
18
19
 
19
20
  # Apply any expand= modifiers to the parsed template
21
+ # @return [Array<Row>]
20
22
  def expand_rows!
21
23
  expanded_rows = []
22
24
  row_index = 0
@@ -32,6 +34,8 @@ module CSVPlusPlus
32
34
  end
33
35
 
34
36
  # Make sure that the template has a valid amount of infinite expand modifiers
37
+ #
38
+ # @param runtime [Runtime] The compiler's current runtime
35
39
  def validate_infinite_expands(runtime)
36
40
  infinite_expand_rows = @rows.filter { |r| r.modifier.expand&.infinite? }
37
41
  return unless infinite_expand_rows.length > 1
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CSVPlusPlus
4
- VERSION = '0.0.5'
4
+ VERSION = '0.1.0'
5
5
  public_constant :VERSION
6
6
  end
@@ -12,17 +12,10 @@ module CSVPlusPlus
12
12
  # write the +template+ to an Excel file
13
13
  def write(template)
14
14
  ::CSVPlusPlus::Writer::RubyXLBuilder.new(
15
- output_filename: @options.output_filename,
15
+ input_filename: @options.output_filename,
16
16
  rows: template.rows,
17
17
  sheet_name: @options.sheet_name
18
- ).write
19
- end
20
-
21
- protected
22
-
23
- def load_requires
24
- require('rubyXL')
25
- require('rubyXL/convenience_methods')
18
+ ).build_workbook.write(@options.output_filename)
26
19
  end
27
20
  end
28
21
  end
@@ -5,41 +5,43 @@ require 'pathname'
5
5
 
6
6
  module CSVPlusPlus
7
7
  module Writer
8
- # A mixin that can
8
+ # A module that can be mixed into any Writer that needs to back up it's @output_filename (all of them except Google
9
+ # Sheets)
9
10
  module FileBackerUpper
10
- # we don't want to include a bunch of second/millisecond stuff in the filename unless we
11
+ # I don't want to include a bunch of second/millisecond stuff in the filename unless we
11
12
  # really need to. so try a less specifically formatted filename then get more specific
12
13
  DESIRED_BACKUP_FORMATS = [%(%Y_%m_%d-%I_%M%p), %(%Y_%m_%d-%I_%M_%S%p), %(%Y_%m_%d-%I_%M_%S_%L%p)].freeze
13
14
  private_constant :DESIRED_BACKUP_FORMATS
14
15
 
15
16
  # Assuming the underlying spreadsheet is file-based, create a backup of it
16
- # rubocop:disable Metrics/MethodLength
17
17
  def write_backup
18
18
  return unless ::File.exist?(@options.output_filename)
19
19
 
20
- attempted = []
21
- backed_up_to = nil
20
+ # TODO: also don't do anything if the current backups contents haven't changed (do a md5sum or something)
22
21
 
23
- # rubocop:disable Lint/ConstantResolution
24
- DESIRED_BACKUP_FORMATS.find do |file_format|
25
- # rubocop:enable Lint/ConstantResolution
26
- filename = format_backup_filename(file_format)
27
- attempted << filename
28
- backed_up_to = backup(filename)
29
-
30
- break if backed_up_to
31
- end
32
-
33
- unless backed_up_to
34
- raise(::CSVPlusPlus::Error, "Unable to write backup file despite trying these: #{attempted.join(', ')}")
22
+ attempt_backups.tap do |backed_up_to|
23
+ warn("Backed up #{@options.output_filename} to #{backed_up_to}") if @options.verbose
35
24
  end
36
-
37
- warn("Backed up #{@options.output_filename} to #{backed_up_to}") if @options.verbose
38
25
  end
39
- # rubocop:enable Metrics/MethodLength
40
26
 
41
27
  private
42
28
 
29
+ def attempt_backups
30
+ attempted =
31
+ # rubocop:disable Lint/ConstantResolution
32
+ DESIRED_BACKUP_FORMATS.map do |file_format|
33
+ # rubocop:enable Lint/ConstantResolution
34
+ filename = format_backup_filename(file_format)
35
+ backed_up_to = backup(filename)
36
+
37
+ next filename unless backed_up_to
38
+
39
+ return backed_up_to
40
+ end
41
+
42
+ raise(::CSVPlusPlus::Error, "Unable to write backup file despite trying these: #{attempted.join(', ')}")
43
+ end
44
+
43
45
  def backup(filename)
44
46
  return if ::File.exist?(filename)
45
47
 
@@ -7,7 +7,7 @@ module CSVPlusPlus
7
7
  # Given +rows+ from a +Template+, build requests compatible with Google Sheets Ruby API
8
8
  # rubocop:disable Metrics/ClassLength
9
9
  class GoogleSheetBuilder
10
- # initialize
10
+ # @param current_sheet_values
11
11
  def initialize(current_sheet_values:, sheet_id:, rows:, column_index: 0, row_index: 0)
12
12
  @current_sheet_values = current_sheet_values
13
13
  @sheet_id = sheet_id
@@ -17,6 +17,8 @@ module CSVPlusPlus
17
17
  end
18
18
 
19
19
  # Build a Google::Apis::SheetsV4::BatchUpdateSpreadsheetRequest
20
+ #
21
+ # @return [Google::Apis::SheetsV4::BatchUpdateSpreadsheetRequest]
20
22
  def batch_update_spreadsheet_request
21
23
  build_batch_request(@rows)
22
24
  end
@@ -42,20 +44,16 @@ module CSVPlusPlus
42
44
  end
43
45
  end
44
46
 
45
- # rubocop:disable Metrics/AbcSize
46
47
  def build_cell_format(mod)
47
48
  sheets_ns::CellFormat.new.tap do |cf|
48
49
  cf.text_format = mod.text_format
49
50
 
50
- cf.horizontal_alignment = mod.halign if mod.halign
51
- cf.vertical_alignment = mod.valign if mod.valign
52
-
53
- cf.background_color = mod.color if mod.color
54
-
55
- cf.number_format = mod.numberformat if mod.numberformat
51
+ cf.horizontal_alignment = mod.halign
52
+ cf.vertical_alignment = mod.valign
53
+ cf.background_color = mod.color
54
+ cf.number_format = mod.numberformat
56
55
  end
57
56
  end
58
- # rubocop:enable Metrics/AbcSize
59
57
 
60
58
  def grid_range_for_cell(cell)
61
59
  sheets_ns::GridRange.new(
@@ -68,7 +66,7 @@ module CSVPlusPlus
68
66
  end
69
67
 
70
68
  def current_value(row_index, cell_index)
71
- @current_values[row_index][cell_index]
69
+ @current_sheet_values[row_index][cell_index]
72
70
  rescue ::StandardError
73
71
  nil
74
72
  end
@@ -12,7 +12,7 @@ module CSVPlusPlus
12
12
  SPREADSHEET_INFINITY = 1000
13
13
  public_constant :SPREADSHEET_INFINITY
14
14
 
15
- # initialize
15
+ # @param options [Options]
16
16
  def initialize(options)
17
17
  super(options)
18
18
 
@@ -21,6 +21,8 @@ module CSVPlusPlus
21
21
  end
22
22
 
23
23
  # write a +template+ to Google Sheets
24
+ #
25
+ # @param template [Template]
24
26
  def write(template)
25
27
  @sheets_client = ::CSVPlusPlus::GoogleApiClient.sheets_client
26
28
 
@@ -38,14 +40,6 @@ module CSVPlusPlus
38
40
  drive_client.copy_file(@sheet_id)
39
41
  end
40
42
 
41
- protected
42
-
43
- def load_requires
44
- require('google/apis/drive_v3')
45
- require('google/apis/sheets_v4')
46
- require('googleauth')
47
- end
48
-
49
43
  private
50
44
 
51
45
  def format_range(range)
@@ -118,7 +112,7 @@ module CSVPlusPlus
118
112
  sheet_id: sheet&.properties&.sheet_id,
119
113
  column_index: @options.offset[1],
120
114
  row_index: @options.offset[0],
121
- current_sheet_values: @current_sheet_values
115
+ current_sheet_values: @current_values
122
116
  )
123
117
  end
124
118
  end
@@ -5,21 +5,29 @@ require_relative './rubyxl_modifier'
5
5
  module CSVPlusPlus
6
6
  module Writer
7
7
  # Build a RubyXL workbook formatted according to the given +rows+
8
+ #
9
+ # @attr_reader input_filename [String] The filename being written to
10
+ # @attr_reader rows [Array<Row>] The rows being written
8
11
  class RubyXLBuilder
9
- attr_reader :output_filename, :rows
12
+ attr_reader :input_filename, :rows
10
13
 
11
- # initialize
12
- def initialize(output_filename:, rows:, sheet_name:)
14
+ # @param input_filename [String] The file to write to
15
+ # @param rows [Array<Row>] The rows to write
16
+ # @param sheet_name [String] The name of the sheet within the workbook to write to
17
+ def initialize(input_filename:, rows:, sheet_name:)
13
18
  @rows = rows
14
- @output_filename = output_filename
15
- @workbook = open_workbook(sheet_name)
16
- @worksheet = @workbook[sheet_name]
19
+ @input_filename = input_filename
20
+ @sheet_name = sheet_name
17
21
  end
18
22
 
19
- # write the given @rows in +sheet_name+ to +@output_filename+
20
- def write
21
- build_workbook!
22
- @workbook.write(@output_filename)
23
+ # Build a +RubyXL::Workbook+ with the given +@rows+ in +sheet_name+
24
+ #
25
+ # @return [RubyXL::Workbook]
26
+ def build_workbook
27
+ open_workbook.tap do |workbook|
28
+ @worksheet = workbook[@sheet_name]
29
+ build_workbook!
30
+ end
23
31
  end
24
32
 
25
33
  private
@@ -96,14 +104,14 @@ module CSVPlusPlus
96
104
  end
97
105
  end
98
106
 
99
- def open_workbook(sheet_name)
100
- if ::File.exist?(@output_filename)
101
- ::RubyXL::Parser.parse(@output_filename).tap do |workbook|
102
- workbook.add_worksheet(sheet_name) unless workbook[sheet_name]
107
+ def open_workbook
108
+ if ::File.exist?(@input_filename)
109
+ ::RubyXL::Parser.parse(@input_filename).tap do |workbook|
110
+ workbook.add_worksheet(@sheet_name) unless workbook[@sheet_name]
103
111
  end
104
112
  else
105
113
  ::RubyXL::Workbook.new.tap do |workbook|
106
- workbook.worksheets[0].sheet_name = sheet_name
114
+ workbook.worksheets[0].sheet_name = @sheet_name
107
115
  end
108
116
  end
109
117
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CSVPlusPlus
4
- # Writer
5
4
  module Writer
6
5
  # Build a RubyXL-decorated Modifier class adds some support for Excel
7
6
  class RubyXLModifier < ::SimpleDelegator
@@ -32,21 +31,29 @@ module CSVPlusPlus
32
31
  }.freeze
33
32
  private_constant :BORDER_STYLES
34
33
 
34
+ # The excel-specific border weight
35
+ #
36
+ # @return [Integer]
37
+ def border_weight
38
+ return unless borderstyle
39
+
40
+ # rubocop:disable Lint/ConstantResolution
41
+ BORDER_STYLES[borderstyle.to_sym]
42
+ # rubocop:enable Lint/ConstantResolution
43
+ end
44
+
35
45
  # The excel-specific number format code
46
+ #
47
+ # @return [String]
36
48
  def number_format_code
49
+ return unless numberformat
50
+
37
51
  ::RubyXL::NumberFormats::DEFAULT_NUMBER_FORMATS.find_by_format_id(
38
52
  # rubocop:disable Lint/ConstantResolution
39
53
  NUM_FMT_IDS[numberformat.to_sym]
40
54
  # rubocop:enable Lint/ConstantResolution
41
55
  ).format_code
42
56
  end
43
-
44
- # The excel-specific border weight
45
- def border_weight
46
- # rubocop:disable Lint/ConstantResolution
47
- BORDER_STYLES[borderstyle.to_sym]
48
- # rubocop:enable Lint/ConstantResolution
49
- end
50
57
  end
51
58
  end
52
59
  end
data/lib/csv_plus_plus.rb CHANGED
@@ -1,19 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'google/apis/drive_v3'
4
+ require 'google/apis/sheets_v4'
5
+ require 'googleauth'
6
+ require 'rubyXL'
7
+ require 'rubyXL/convenience_methods'
8
+
3
9
  require_relative 'csv_plus_plus/cli'
4
10
  require_relative 'csv_plus_plus/error'
11
+ require_relative 'csv_plus_plus/language/builtins'
5
12
  require_relative 'csv_plus_plus/language/compiler'
13
+ require_relative 'csv_plus_plus/language/runtime'
6
14
  require_relative 'csv_plus_plus/options'
7
15
  require_relative 'csv_plus_plus/writer'
8
16
 
9
- # A language for writing rich CSV data
17
+ # A programming language for writing rich CSV files
10
18
  module CSVPlusPlus
11
19
  # Parse the input into a +Template+ and write it to the desired format
20
+ #
21
+ # @param input [String] The csvpp input to compile
22
+ # @param filename [String, nil] The filename the input was read from. +nil+ if it is read from stdin.
23
+ # @param options [Options] The various options to compile with
24
+ #
25
+ # rubocop:disable Metrics/MethodLength
12
26
  def self.apply_template_to_sheet!(input, filename, options)
13
27
  warn(options.verbose_summary) if options.verbose
14
28
 
15
- ::CSVPlusPlus::Language::Compiler.with_compiler(input:, filename:, options:) do |c|
16
- template = c.parse_template
29
+ ::CSVPlusPlus::Language::Compiler.with_compiler(
30
+ options:,
31
+ runtime: ::CSVPlusPlus::Language::Runtime.new(input:, filename:)
32
+ ) do |c|
33
+ template = c.compile_template
17
34
 
18
35
  output = ::CSVPlusPlus::Writer.writer(options)
19
36
  c.outputting! do
@@ -22,4 +39,5 @@ module CSVPlusPlus
22
39
  end
23
40
  end
24
41
  end
42
+ # rubocop:enable Metrics/MethodLength
25
43
  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.0.5
4
+ version: 0.1.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-02-18 00:00:00.000000000 Z
11
+ date: 2023-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: google-apis-drive_v3
@@ -133,6 +133,9 @@ files:
133
133
  - lib/csv_plus_plus/google_api_client.rb
134
134
  - lib/csv_plus_plus/google_options.rb
135
135
  - 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
136
139
  - lib/csv_plus_plus/language/cell_value.tab.rb
137
140
  - lib/csv_plus_plus/language/code_section.tab.rb
138
141
  - lib/csv_plus_plus/language/compiler.rb