csv_plus_plus 0.0.5 → 0.1.0

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 (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