csv_plus_plus 0.1.2 → 0.1.3

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -2
  3. data/{CHANGELOG.md → docs/CHANGELOG.md} +9 -0
  4. data/lib/csv_plus_plus/benchmarked_compiler.rb +70 -20
  5. data/lib/csv_plus_plus/cell.rb +46 -24
  6. data/lib/csv_plus_plus/cli.rb +23 -13
  7. data/lib/csv_plus_plus/cli_flag.rb +1 -2
  8. data/lib/csv_plus_plus/color.rb +32 -7
  9. data/lib/csv_plus_plus/compiler.rb +82 -60
  10. data/lib/csv_plus_plus/entities/ast_builder.rb +27 -43
  11. data/lib/csv_plus_plus/entities/boolean.rb +18 -9
  12. data/lib/csv_plus_plus/entities/builtins.rb +23 -9
  13. data/lib/csv_plus_plus/entities/cell_reference.rb +200 -29
  14. data/lib/csv_plus_plus/entities/date.rb +38 -5
  15. data/lib/csv_plus_plus/entities/entity.rb +27 -61
  16. data/lib/csv_plus_plus/entities/entity_with_arguments.rb +57 -0
  17. data/lib/csv_plus_plus/entities/function.rb +23 -11
  18. data/lib/csv_plus_plus/entities/function_call.rb +24 -9
  19. data/lib/csv_plus_plus/entities/number.rb +24 -10
  20. data/lib/csv_plus_plus/entities/runtime_value.rb +22 -5
  21. data/lib/csv_plus_plus/entities/string.rb +19 -6
  22. data/lib/csv_plus_plus/entities/variable.rb +16 -4
  23. data/lib/csv_plus_plus/entities.rb +20 -13
  24. data/lib/csv_plus_plus/error/error.rb +11 -1
  25. data/lib/csv_plus_plus/error/formula_syntax_error.rb +1 -0
  26. data/lib/csv_plus_plus/error/modifier_syntax_error.rb +53 -5
  27. data/lib/csv_plus_plus/error/modifier_validation_error.rb +34 -14
  28. data/lib/csv_plus_plus/error/syntax_error.rb +22 -9
  29. data/lib/csv_plus_plus/error/writer_error.rb +8 -0
  30. data/lib/csv_plus_plus/error.rb +1 -0
  31. data/lib/csv_plus_plus/google_api_client.rb +7 -2
  32. data/lib/csv_plus_plus/google_options.rb +23 -18
  33. data/lib/csv_plus_plus/lexer/lexer.rb +8 -4
  34. data/lib/csv_plus_plus/lexer/tokenizer.rb +6 -1
  35. data/lib/csv_plus_plus/lexer.rb +24 -0
  36. data/lib/csv_plus_plus/modifier/conditional_formatting.rb +1 -0
  37. data/lib/csv_plus_plus/modifier/data_validation.rb +138 -0
  38. data/lib/csv_plus_plus/modifier/expand.rb +61 -0
  39. data/lib/csv_plus_plus/modifier/google_sheet_modifier.rb +133 -0
  40. data/lib/csv_plus_plus/modifier/modifier.rb +222 -0
  41. data/lib/csv_plus_plus/modifier/modifier_validator.rb +243 -0
  42. data/lib/csv_plus_plus/modifier/rubyxl_modifier.rb +84 -0
  43. data/lib/csv_plus_plus/modifier.rb +82 -158
  44. data/lib/csv_plus_plus/options.rb +64 -19
  45. data/lib/csv_plus_plus/parser/cell_value.tab.rb +5 -5
  46. data/lib/csv_plus_plus/parser/code_section.tab.rb +8 -13
  47. data/lib/csv_plus_plus/parser/modifier.tab.rb +17 -23
  48. data/lib/csv_plus_plus/row.rb +53 -12
  49. data/lib/csv_plus_plus/runtime/can_define_references.rb +87 -0
  50. data/lib/csv_plus_plus/runtime/can_resolve_references.rb +209 -0
  51. data/lib/csv_plus_plus/runtime/graph.rb +68 -0
  52. data/lib/csv_plus_plus/runtime/position_tracker.rb +231 -0
  53. data/lib/csv_plus_plus/runtime/references.rb +110 -0
  54. data/lib/csv_plus_plus/runtime/runtime.rb +126 -0
  55. data/lib/csv_plus_plus/runtime.rb +34 -191
  56. data/lib/csv_plus_plus/source_code.rb +66 -0
  57. data/lib/csv_plus_plus/template.rb +62 -35
  58. data/lib/csv_plus_plus/version.rb +2 -1
  59. data/lib/csv_plus_plus/writer/base_writer.rb +30 -5
  60. data/lib/csv_plus_plus/writer/csv.rb +11 -9
  61. data/lib/csv_plus_plus/writer/excel.rb +9 -2
  62. data/lib/csv_plus_plus/writer/file_backer_upper.rb +1 -0
  63. data/lib/csv_plus_plus/writer/google_sheet_builder.rb +71 -23
  64. data/lib/csv_plus_plus/writer/google_sheets.rb +79 -29
  65. data/lib/csv_plus_plus/writer/open_document.rb +6 -1
  66. data/lib/csv_plus_plus/writer/rubyxl_builder.rb +103 -30
  67. data/lib/csv_plus_plus/writer.rb +39 -9
  68. data/lib/csv_plus_plus.rb +29 -12
  69. metadata +18 -14
  70. data/lib/csv_plus_plus/can_define_references.rb +0 -88
  71. data/lib/csv_plus_plus/can_resolve_references.rb +0 -8
  72. data/lib/csv_plus_plus/data_validation.rb +0 -138
  73. data/lib/csv_plus_plus/expand.rb +0 -20
  74. data/lib/csv_plus_plus/graph.rb +0 -62
  75. data/lib/csv_plus_plus/references.rb +0 -68
  76. data/lib/csv_plus_plus/scope.rb +0 -196
  77. data/lib/csv_plus_plus/validated_modifier.rb +0 -164
  78. data/lib/csv_plus_plus/writer/google_sheet_modifier.rb +0 -77
  79. data/lib/csv_plus_plus/writer/rubyxl_modifier.rb +0 -59
@@ -1,59 +1,69 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
- require_relative 'benchmarked_compiler'
4
- require_relative 'entities'
5
- require_relative 'parser/code_section.tab'
6
- require_relative 'runtime'
7
- require_relative 'scope'
8
-
9
4
  module CSVPlusPlus
10
5
  # Encapsulates the parsing and building of objects (+Template+ -> +Row+ -> +Cell+). Variable resolution is delegated
11
6
  # to the +Scope+
12
7
  #
13
8
  # @attr_reader options [Options] The +Options+ to compile with
14
9
  # @attr_reader runtime [Runtime] The runtime execution
15
- # @attr_reader scope [Scope] +Scope+ for variable resolution
10
+ # rubocop:disable Metrics/ClassLength
16
11
  class Compiler
17
- attr_reader :options, :runtime, :scope
12
+ extend ::T::Sig
13
+
14
+ sig { returns(::CSVPlusPlus::Options) }
15
+ attr_reader :options
16
+
17
+ sig { returns(::CSVPlusPlus::Runtime::Runtime) }
18
+ attr_reader :runtime
18
19
 
20
+ sig do
21
+ params(
22
+ options: ::CSVPlusPlus::Options,
23
+ runtime: ::CSVPlusPlus::Runtime::Runtime,
24
+ block: ::T.proc.params(arg0: ::CSVPlusPlus::Compiler).void
25
+ ).void
26
+ end
19
27
  # Create a compiler and make sure it gets cleaned up
20
28
  #
21
- # @param runtime [Runtime] The initial +Runtime+ for the compiler
22
29
  # @param options [Options]
23
- def self.with_compiler(runtime:, options:, &block)
24
- compiler = new(options:, runtime:)
30
+ # @param runtime [Runtime] The initial +Runtime+ for the compiler
31
+ def self.with_compiler(options:, runtime:, &block)
25
32
  if options.verbose
26
- ::CSVPlusPlus::BenchmarkedCompiler.with_benchmarks(compiler) do |c|
33
+ ::CSVPlusPlus::BenchmarkedCompiler.with_benchmarks(options:, runtime:) do |c|
27
34
  block.call(c)
28
35
  end
29
36
  else
30
- yield(compiler)
37
+ block.call(new(options:, runtime:))
31
38
  end
32
39
  ensure
33
40
  runtime.cleanup!
34
41
  end
35
42
 
36
- # @param runtime [Runtime]
43
+ sig { params(options: ::CSVPlusPlus::Options, runtime: ::CSVPlusPlus::Runtime::Runtime).void }
37
44
  # @param options [Options]
38
- # @param scope [Scope, nil]
39
- def initialize(runtime:, options:, scope: nil)
45
+ # @param runtime [Runtime]
46
+ def initialize(options:, runtime:)
40
47
  @options = options
41
48
  @runtime = runtime
42
- @scope = scope || ::CSVPlusPlus::Scope.new(runtime:)
43
49
 
44
50
  # TODO: infer a type
45
51
  # allow user-supplied key/values to override anything global or from the code section
46
- @scope.def_variables(
52
+ @runtime.def_variables(
47
53
  options.key_values.transform_values { |v| ::CSVPlusPlus::Entities::String.new(v.to_s) }
48
54
  )
49
55
  end
50
56
 
51
- # Write the compiled results
52
- def outputting!
53
- @runtime.start_at_csv!
54
- yield
57
+ sig { params(benchmark: ::Benchmark::Report).void }
58
+ # Attach a +Benchmark+ and a place to store timings to the compiler class.
59
+ #
60
+ # @param benchmark [Benchmark] A +Benchmark+ instance
61
+ def benchmark=(benchmark)
62
+ @benchmark = ::T.let(benchmark, ::T.nilable(::Benchmark::Report))
63
+ @timings = ::T.let([], ::T.nilable(::T::Array[::Benchmark::Tms]))
55
64
  end
56
65
 
66
+ sig { returns(::CSVPlusPlus::Template) }
57
67
  # Compile a template and return a +::CSVPlusPlus::Template+ instance ready to be written with a +Writer+
58
68
  #
59
69
  # @return [Template]
@@ -61,96 +71,108 @@ module CSVPlusPlus
61
71
  parse_code_section!
62
72
  rows = parse_csv_section!
63
73
 
64
- ::CSVPlusPlus::Template.new(rows:, scope: @scope).tap do |t|
74
+ ::CSVPlusPlus::Template.new(rows:, runtime: @runtime).tap do |t|
65
75
  t.validate_infinite_expands(@runtime)
66
- expanding { t.expand_rows! }
76
+ expanding! { t.expand_rows! }
77
+ bind_all_vars! { t.bind_all_vars!(@runtime) }
67
78
  resolve_all_cells!(t)
68
79
  end
69
80
  end
70
81
 
71
- # @return [String]
72
- def to_s
73
- "Compiler(options: #{@options}, runtime: #{@runtime}, scope: #{@scope})"
82
+ sig { params(block: ::T.proc.params(runtime: ::CSVPlusPlus::Runtime::Runtime).void).void }
83
+ # Write the compiled results
84
+ def outputting!(&block)
85
+ @runtime.start_at_csv! { block.call(@runtime) }
74
86
  end
75
87
 
76
88
  protected
77
89
 
78
- # Parses the input file and returns a +CodeSection+
79
- #
80
- # @return [CodeSection]
90
+ sig { void }
91
+ # Parses the input file and sets variables on +@runtime+ as necessary
81
92
  def parse_code_section!
82
- @runtime.start!
83
-
84
- # TODO: this flow can probably be refactored, it used to have more needs back when we had to
85
- # parse and save the code_section
86
- parsing_code_section do |input|
87
- csv_section = ::CSVPlusPlus::Parser::CodeSection.new(@scope).parse(input, @runtime)
88
- # TODO: call scope.resolve_static_variables?? or maybe it doesn't matter
89
-
90
- # return the csv_section to the caller because they're gonna re-write input with it
91
- next csv_section
93
+ @runtime.start! do
94
+ # TODO: this flow can probably be refactored, it used to have more needs back when we had to
95
+ # parse and save the code_section
96
+ parsing_code_section do |input|
97
+ csv_section = ::CSVPlusPlus::Parser::CodeSection.new.parse(input, @runtime)
98
+
99
+ # return the csv_section to the caller because they're gonna re-write input with it
100
+ next csv_section
101
+ end
92
102
  end
93
- # @scope.code_section
94
103
  end
95
104
 
105
+ sig { returns(::T::Array[::CSVPlusPlus::Row]) }
96
106
  # Parse the CSV section and return an array of +Row+s
97
107
  #
98
108
  # @return [Array<Row>]
99
109
  def parse_csv_section!
100
- @runtime.start_at_csv!
101
- @runtime.map_rows(::CSV.new(runtime.input)) do |csv_row|
102
- parse_row(csv_row)
110
+ @runtime.start_at_csv! do
111
+ @runtime.map_lines(::CSV.new(::T.unsafe(@runtime.input))) do |csv_row|
112
+ parse_row(::T.cast(csv_row, ::T::Array[::String]))
113
+ end
103
114
  end
104
115
  ensure
105
116
  # we're done with the file and everything is in memory
106
117
  @runtime.cleanup!
107
118
  end
108
119
 
120
+ sig { params(template: ::CSVPlusPlus::Template).returns(::T::Array[::T::Array[::CSVPlusPlus::Entities::Entity]]) }
109
121
  # Iterates through each cell of each row and resolves it's variable and function references.
110
122
  #
111
123
  # @param template [Template]
124
+ #
112
125
  # @return [Array<Entity>]
113
126
  def resolve_all_cells!(template)
114
- @runtime.start_at_csv!
115
- @runtime.map_rows(template.rows, cells_too: true) do |cell|
116
- cell.ast = @scope.resolve_cell_value if cell.ast
127
+ @runtime.start_at_csv! do
128
+ @runtime.map_all_cells(template.rows) do |cell|
129
+ cell.ast = @runtime.resolve_cell_value if cell.ast
130
+ end
117
131
  end
118
132
  end
119
133
 
134
+ sig { params(block: ::T.proc.void).void }
120
135
  # Expanding rows
121
- def expanding
122
- @runtime.start_at_csv!
123
- yield
136
+ def expanding!(&block)
137
+ @runtime.start_at_csv! { block.call }
138
+ end
139
+
140
+ sig { params(block: ::T.proc.void).void }
141
+ # Binding all [[var=]] directives
142
+ def bind_all_vars!(&block)
143
+ @runtime.start_at_csv! { block.call }
124
144
  end
125
145
 
126
146
  private
127
147
 
128
- def parsing_code_section
129
- csv_section = yield(@runtime.input.read)
148
+ sig { params(block: ::T.proc.params(arg0: ::String).returns(::String)).void }
149
+ def parsing_code_section(&block)
150
+ csv_section = block.call(::T.must(::T.must(@runtime.input).read))
130
151
  @runtime.rewrite_input!(csv_section)
131
152
  end
132
153
 
154
+ sig { params(csv_row: ::T::Array[::T.nilable(::String)]).returns(::CSVPlusPlus::Row) }
133
155
  # Using the current +@runtime+ and the given +csv_row+ parse it into a +Row+ of +Cell+s
134
156
  # +csv_row+ should have already been run through a CSV parser and is an array of strings
135
157
  #
136
158
  # @param csv_row [Array<Array<String>>]
159
+ #
137
160
  # @return [Row]
138
161
  def parse_row(csv_row)
139
- row_modifier = ::CSVPlusPlus::ValidatedModifier.new(row_level: true)
162
+ row_modifier = ::CSVPlusPlus::Modifier.new(@options, row_level: true)
140
163
 
141
- cells = @runtime.map_row(csv_row) { |value, _cell_index| parse_cell(value, row_modifier) }
164
+ cells = @runtime.map_row(csv_row) { |value, _cell_index| parse_cell(value || '', row_modifier) }
142
165
 
143
- ::CSVPlusPlus::Row.new(@runtime.row_index, cells, row_modifier)
166
+ ::CSVPlusPlus::Row.new(cells:, index: @runtime.row_index, modifier: row_modifier)
144
167
  end
145
168
 
169
+ sig { params(value: ::String, row_modifier: ::CSVPlusPlus::Modifier::Modifier).returns(::CSVPlusPlus::Cell) }
146
170
  def parse_cell(value, row_modifier)
147
- cell_modifier = ::CSVPlusPlus::ValidatedModifier.new
148
- parsed_value = ::CSVPlusPlus::Parser::Modifier.new(cell_modifier:, row_modifier:, scope: @scope).parse(
149
- value,
150
- @runtime
151
- )
171
+ cell_modifier = ::CSVPlusPlus::Modifier.new(@options)
172
+ parsed_value = ::CSVPlusPlus::Parser::Modifier.new(cell_modifier:, row_modifier:).parse(value, @runtime)
152
173
 
153
174
  ::CSVPlusPlus::Cell.parse(parsed_value, runtime:, modifier: cell_modifier)
154
175
  end
155
176
  end
177
+ # rubocop:enable Metrics/ClassLength
156
178
  end
@@ -1,65 +1,49 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module CSVPlusPlus
4
5
  module Entities
5
6
  # Some helpful functions that can be mixed into a class to help building ASTs
6
7
  module ASTBuilder
8
+ extend ::T::Sig
9
+
10
+ sig do
11
+ params(
12
+ method_name: ::Symbol,
13
+ args: ::T.untyped,
14
+ kwargs: ::T.untyped,
15
+ block: ::T.untyped
16
+ ).returns(::CSVPlusPlus::Entities::Entity)
17
+ end
7
18
  # Let the current class have functions which can build a given entity by calling it's type. For example
8
19
  # +number(1)+, +variable(:foo)+
9
20
  #
10
21
  # @param method_name [Symbol] The +method_name+ to respond to
11
- # @param arguments [] The arguments to create the entity with
22
+ # @param args [Array] The arguments to create the entity with
23
+ # @param kwargs [Hash] The arguments to create the entity with
12
24
  #
13
25
  # @return [Entity, #super]
14
- def method_missing(method_name, *args, **kwargs, &)
15
- entity_class = ::CSVPlusPlus::Entities::TYPES[method_name.to_sym]
16
- return super unless entity_class
17
-
18
- entity_class.new(*args, **kwargs, &)
26
+ # rubocop:disable Naming/BlockForwarding
27
+ def method_missing(method_name, *args, **kwargs, &block)
28
+ entity_class_name = method_name.to_s.split('_').map(&:capitalize).join.to_sym
29
+ ::CSVPlusPlus::Entities.const_get(entity_class_name).new(*args, **kwargs, &block)
30
+ rescue ::NameError
31
+ super
19
32
  end
33
+ # rubocop:enable Naming/BlockForwarding
20
34
 
35
+ sig { params(method_name: ::Symbol, _args: ::T.untyped).returns(::T::Boolean) }
21
36
  # Let the current class have functions which can build a given entity by calling it's type. For example
22
37
  # +number(1)+, +variable(:foo)+
23
38
  #
24
39
  # @param method_name [Symbol] The +method_name+ to respond to
25
- # @param arguments [] The arguments to create the entity with
40
+ # @param _args [::T.Untyped] The arguments to create the entity with
26
41
  #
27
- # @return [Boolean, #super]
28
- def respond_to_missing?(method_name, *_arguments)
29
- ::CSVPlusPlus::Entities::TYPES.include?(method_name.to_sym) || super
30
- end
31
-
32
- # Turns index-based/X,Y coordinates into a A1 format
33
- #
34
- # @param row_index [Integer]
35
- # @param cell_index [Integer]
36
- #
37
- # @return [String]
38
- def ref(row_index: nil, cell_index: nil)
39
- return unless row_index || cell_index
40
-
41
- rowref = row_index ? (row_index + 1).to_s : ''
42
- cellref = cell_index ? cell_ref(cell_index) : ''
43
- cell_reference([cellref, rowref].join)
44
- end
45
-
46
- private
47
-
48
- ALPHA = ('A'..'Z').to_a.freeze
49
- private_constant :ALPHA
50
-
51
- def cell_ref(cell_index)
52
- c = cell_index.dup
53
- ref = ''
54
-
55
- while c >= 0
56
- # rubocop:disable Lint/ConstantResolution
57
- ref += ALPHA[c % 26]
58
- # rubocop:enable Lint/ConstantResolution
59
- c = (c / 26).floor - 1
60
- end
61
-
62
- ref.reverse
42
+ # @return [::T::Boolean, #super]
43
+ def respond_to_missing?(method_name, *_args)
44
+ !::CSVPlusPlus::Entities::Type.deserialize(method_name.to_s.gsub('_', '')).nil?
45
+ rescue ::KeyError
46
+ super
63
47
  end
64
48
  end
65
49
  end
@@ -1,30 +1,39 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
- require_relative './entity'
4
-
5
4
  module CSVPlusPlus
6
5
  module Entities
7
6
  # A boolean value
8
7
  #
9
8
  # @attr_reader value [true, false]
10
9
  class Boolean < Entity
10
+ sig { returns(::T::Boolean) }
11
11
  attr_reader :value
12
12
 
13
- # @param value [String, Boolean]
13
+ sig { params(value: ::T.any(::String, ::T::Boolean)).void }
14
+ # @param value [::String, boolean]
14
15
  def initialize(value)
15
- super(:boolean)
16
+ super(::CSVPlusPlus::Entities::Type::Boolean)
16
17
  # TODO: probably can do a lot better in general on type validation
17
- @value = value.is_a?(::String) ? (value.downcase == 'true') : value
18
+ @value = ::T.let(value.is_a?(::String) ? (value.downcase == 'true') : value, ::T::Boolean)
18
19
  end
19
20
 
20
- # @return [String]
21
- def to_s
21
+ sig { override.params(_runtime: ::CSVPlusPlus::Runtime::Runtime).returns(::String) }
22
+ # @param _runtime [Runtime]
23
+ #
24
+ # @return [::String]
25
+ def evaluate(_runtime)
22
26
  @value.to_s.upcase
23
27
  end
24
28
 
25
- # @return [boolean]
29
+ sig { override.params(other: ::CSVPlusPlus::Entities::Entity).returns(::T::Boolean) }
30
+ # @param other [Entity]
31
+ #
32
+ # @return [::T::Boolean]
26
33
  def ==(other)
27
- super && value == other.value
34
+ return false unless super
35
+
36
+ other.is_a?(self.class) && value == other.value
28
37
  end
29
38
  end
30
39
  end
@@ -1,3 +1,4 @@
1
+ # typed: false
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module CSVPlusPlus
@@ -8,35 +9,48 @@ module CSVPlusPlus
8
9
 
9
10
  VARIABLES = {
10
11
  # The number (integer) of the current cell. Starts at 1
11
- cellnum: runtime_value(->(runtime) { number(runtime.cell_index + 1) }),
12
+ cellnum: runtime_value(->(r) { number(r.cell_index + 1) }),
12
13
 
13
14
  # A reference to the current cell
14
- cellref: runtime_value(->(runtime) { ref(row_index: runtime.row_index, cell_index: runtime.cell_index) }),
15
+ cellref: runtime_value(->(r) { cell_reference(row_index: r.row_index, cell_index: r.cell_index) }),
15
16
 
16
17
  # A reference to the row above
17
- rowabove: runtime_value(->(runtime) { ref(row_index: [0, (runtime.row_index - 1)].max) }),
18
+ rowabove: runtime_value(->(r) { cell_reference(row_index: [0, (r.row_index - 1)].max) }),
18
19
 
19
20
  # A reference to the row below
20
- rowbelow: runtime_value(->(runtime) { ref(row_index: runtime.row_index + 1) }),
21
+ rowbelow: runtime_value(->(r) { cell_reference(row_index: r.row_index + 1) }),
21
22
 
22
23
  # The number (integer) of the current row. Starts at 1
23
- rownum: runtime_value(->(runtime) { number(runtime.rownum) }),
24
+ rownum: runtime_value(->(r) { number(r.rownum) }),
24
25
 
25
26
  # A reference to the current row
26
- rowref: runtime_value(->(runtime) { ref(row_index: runtime.row_index) })
27
+ rowref: runtime_value(->(r) { cell_reference(row_index: r.row_index) })
27
28
  }.freeze
28
29
  public_constant :VARIABLES
29
30
 
30
31
  FUNCTIONS = {
31
32
  # TODO: A reference to a cell in a given row?
32
33
  # A reference to a cell above the current row
33
- cellabove: runtime_value(->(runtime, args) { cell_reference([args[0], [1, (runtime.rownum - 1)].max].join) }),
34
+ # cellabove: runtime_value(->(r, args) { cell_reference(ref: [args[0], [1, (r.rownum - 1)].max].join) }),
35
+ cellabove: runtime_value(
36
+ lambda { |r, args|
37
+ cell_reference(cell_index: args[0].cell_index, row_index: [0, (r.row_index - 1)].max)
38
+ }
39
+ ),
34
40
 
35
41
  # A reference to a cell in the current row
36
- celladjacent: runtime_value(->(runtime, args) { cell_reference([args[0], runtime.rownum].join) }),
42
+ celladjacent: runtime_value(
43
+ lambda { |r, args|
44
+ cell_reference(cell_index: args[0].cell_index, row_index: r.row_index)
45
+ }
46
+ ),
37
47
 
38
48
  # A reference to a cell below the current row
39
- cellbelow: runtime_value(->(runtime, args) { cell_reference([args[0], runtime.rownum + 1].join) })
49
+ cellbelow: runtime_value(
50
+ lambda { |r, args|
51
+ cell_reference(cell_index: args[0].cell_index, row_index: r.row_index + 1)
52
+ }
53
+ )
40
54
  }.freeze
41
55
  public_constant :FUNCTIONS
42
56
  end