Class: CSVPlusPlus::Language::Compiler

Inherits:
Object
  • Object
show all
Defined in:
lib/csv_plus_plus/language/compiler.rb

Overview

Encapsulates the parsing and building of objects (Template -> Row -> Cell). Variable resolution is delegated to the Scope rubocop:disable Metrics/ClassLength

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(runtime:, options:, scope: nil, benchmark: nil) ⇒ Compiler

initialize



49
50
51
52
53
54
55
# File 'lib/csv_plus_plus/language/compiler.rb', line 49

def initialize(runtime:, options:, scope: nil, benchmark: nil)
  @options = options
  @runtime = runtime
  @scope = scope || ::CSVPlusPlus::Language::Scope.new(runtime:)
  @benchmark = benchmark
  @timings = [] if benchmark
end

Instance Attribute Details

#benchmarkObject (readonly)

Returns the value of attribute benchmark.



22
23
24
# File 'lib/csv_plus_plus/language/compiler.rb', line 22

def benchmark
  @benchmark
end

#optionsObject (readonly)

Returns the value of attribute options.



22
23
24
# File 'lib/csv_plus_plus/language/compiler.rb', line 22

def options
  @options
end

#runtimeObject (readonly)

Returns the value of attribute runtime.



22
23
24
# File 'lib/csv_plus_plus/language/compiler.rb', line 22

def runtime
  @runtime
end

#scopeObject (readonly)

Returns the value of attribute scope.



22
23
24
# File 'lib/csv_plus_plus/language/compiler.rb', line 22

def scope
  @scope
end

#timingsObject (readonly)

Returns the value of attribute timings.



22
23
24
# File 'lib/csv_plus_plus/language/compiler.rb', line 22

def timings
  @timings
end

Class Method Details

.compiler_with_timings(options:, runtime:, &block) ⇒ Object

Create a compiler that can time each of it’s stages



40
41
42
43
44
45
46
# File 'lib/csv_plus_plus/language/compiler.rb', line 40

def self.compiler_with_timings(options:, runtime:, &block)
  ::Benchmark.benchmark(::Benchmark::CAPTION, 25, ::Benchmark::FORMAT, '> Total') do |x|
    compiler = new(options:, runtime:, benchmark: x)
    block.call(compiler)
    [compiler.timings.reduce(:+)]
  end
end

.with_compiler(input:, filename:, options:, &block) ⇒ Object

Create a compiler and make sure it gets cleaned up



25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/csv_plus_plus/language/compiler.rb', line 25

def self.with_compiler(input:, filename:, options:, &block)
  runtime = ::CSVPlusPlus::Language::Runtime.new(filename:, input:)

  if options.verbose
    compiler_with_timings(runtime:, options:) do |c|
      block.call(c)
    end
  else
    yield(new(runtime:, options:))
  end
ensure
  runtime.cleanup!
end

Instance Method Details

#outputting!(&block) ⇒ Object

workflow when writing results



124
125
126
127
128
# File 'lib/csv_plus_plus/language/compiler.rb', line 124

def outputting!(&block)
  workflow(stage: 'Writing the spreadsheet') do
    block.call
  end
end

#parse_code_section!Object

parses the input file and returns a CodeSection



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/csv_plus_plus/language/compiler.rb', line 70

def parse_code_section!
  parsing_code_section do |input|
    code_section, csv_section = ::CSVPlusPlus::Language::CodeSectionParser.new.parse(input, self)
    # TODO: infer a type
    # allow user-supplied key/values to override anything global or from the code section
    code_section.def_variables(
      options.key_values.transform_values { |v| ::CSVPlusPlus::Language::Entities::String.new(v.to_s) }
    )
    @scope.code_section = code_section

    # return the csv_section to the caller because they're gonna re-write input with it
    next csv_section
  end
  @scope.code_section
end

#parse_csv_section!Object

workflow when parsing csv



87
88
89
90
91
92
93
94
95
96
# File 'lib/csv_plus_plus/language/compiler.rb', line 87

def parse_csv_section!
  workflow(stage: 'Parsing CSV section') do
    @runtime.map_rows(::CSV.new(runtime.input)) do |csv_row|
      parse_row(csv_row)
    end
  end
ensure
  # we're done with the file and everything is in memory
  @runtime.cleanup!
end

#parse_row(csv_row) ⇒ Object

Using the current @runtime and the given csv_row parse it into a Row of Cells csv_row should have already been run through a CSV parser and is an array of strings



100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/csv_plus_plus/language/compiler.rb', line 100

def parse_row(csv_row)
  row_modifier = ::CSVPlusPlus::Modifier.new(row_level: true)

  cells =
    @runtime.map_row(csv_row) do |value, _cell_index|
      cell_modifier = ::CSVPlusPlus::Modifier.new
      parsed_value = ::CSVPlusPlus::ModifierParser.new(row_modifier:, cell_modifier:).parse(value, @runtime)

      ::CSVPlusPlus::Cell.parse(parsed_value, runtime:, modifier: cell_modifier)
    end

  ::CSVPlusPlus::Row.new(@runtime.row_index, cells, row_modifier)
end

#parse_templateObject

Parse an entire template and return a ::CSVPlusPlus::Template instance



58
59
60
61
62
63
64
65
66
67
# File 'lib/csv_plus_plus/language/compiler.rb', line 58

def parse_template
  parse_code_section!
  rows = parse_csv_section!

  ::CSVPlusPlus::Template.new(rows:, scope: @scope).tap do |t|
    t.validate_infinite_expands(@runtime)
    expanding { t.expand_rows! }
    resolve_all_cells!(t)
  end
end

#resolve_all_cells!(template) ⇒ Object

workflow when resolving the values of all cells



115
116
117
118
119
120
121
# File 'lib/csv_plus_plus/language/compiler.rb', line 115

def resolve_all_cells!(template)
  workflow(stage: 'Resolving each cell') do
    @runtime.map_rows(template.rows, cells_too: true) do |cell|
      cell.ast = @scope.resolve_cell_value if cell.ast
    end
  end
end

#to_sObject

to_s



131
132
133
# File 'lib/csv_plus_plus/language/compiler.rb', line 131

def to_s
  "Compiler(options: #{@options}, runtime: #{@runtime}, scope: #{@scope})"
end